En regardant une requête particulièrement ennuyeuse sur les tables MyISAM qui prend beaucoup de temps à exécuter à plusieurs reprises, j'ai remarqué que MySQL semble exposer un modèle d'E / S plutôt étrange: lors de l'exécution d'une seule requête et de devoir effectuer une importante quantité d'E / S (par exemple pour une analyse de table ou lorsque les caches sont vides en raison de echo 3 > /proc/sys/vm/drop_caches
la nécessité de charger d'abord les index sur le disque), la taille de la file d'attente pour le périphérique sous-jacent est proche de la valeur 1, avec des performances abyssales de seulement 4-5 Mo / s:
root@mysql-test:~# iostat -xdm 5 /dev/sda
Linux 3.2.0-40-generic (mysql-test) 04/30/2014 _x86_64_ (4 CPU)
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.14 24.82 18.26 88.79 0.75 4.61 102.56 2.83 26.39 19.29 27.85 2.46 26.31
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 69.29 151.52 72.73 5.31 0.59 53.95 1.21 5.39 7.84 0.29 4.39 98.51
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 153.06 144.29 174.69 4.96 1.36 40.54 1.39 4.36 8.91 0.60 3.15 100.49
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 105.75 150.92 109.03 4.53 0.85 42.41 1.29 4.96 8.15 0.54 3.90 101.36
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 48.89 156.36 51.72 5.28 0.76 59.38 1.28 6.16 8.02 0.55 4.77 99.23
Alors que les 150 IOPS sont tout simplement ce qu'un seul disque dans la configuration donnée est capable de fournir en termes d'E / S aléatoires, le résultat me surprend vraiment car je m'attendrais à ce que MySQL soit capable d'exécuter des E / S asynchrones pour les lectures et récupérer un grande quantité de blocs simultanément au lieu de les lire et de les évaluer un par un, en négligeant efficacement les gains de parallélisation disponibles dans les configurations RAID. Quelle décision de conception ou option de configuration en est responsable? Est-ce un problème spécifique à la plate-forme?
Bien que j'aie testé cela avec des tables MyISAM de grande taille, je vois des effets similaires avec les mêmes tables converties en InnoDB (bien que ce ne soit pas aussi mauvais, l'exemple de requête prend toujours 20 à 30 secondes, la plupart du temps étant consacré à la lecture du disque avec une longueur de file d'attente de 1) après avoir redémarré le démon mysql et donc les pools de mémoire tampon sont vides. J'ai également vérifié que le même problème persiste sur 5.6 GA et le jalon actuel 5.7 5.7 - tant que j'utilise un seul thread de requête, MySQL semble incapable de paralléliser les opérations d'E / S nécessaires au traitement des requêtes.
Selon la demande, quelques détails supplémentaires sur le scénario. Le comportement peut être observé avec une multitude de types de requêtes. J'en ai arbitrairement choisi un pour des tests supplémentaires qui se lit un peu comme ceci:
SELECT herp.id, herp.firstname, herp.lastname, derp.label, herp.email,
(SELECT CONCAT(label, " (", zip_code, " ", city,")" ) FROM subsidiaries WHERE subsidiaries.id=herp.subsidiary_id ) AS subsidiary,
(SELECT COUNT(fk_herp) from herp_missing_data WHERE fk_herp=herp.id) AS missing_data
FROM herp LEFT JOIN derp ON derp.id=herp.fk_derp
WHERE (herp.fk_pools='123456') AND herp.city LIKE '%Some City%' AND herp.active='yes'
ORDER BY herp.id desc LIMIT 0,10;
Je sais qu'il a une certaine marge d'optimisation, mais j'ai décidé de m'en tenir à cela pour un certain nombre de raisons et de me concentrer sur la recherche d'une explication générale du modèle d'E / S inattendu que je vois.
Les tables utilisées contiennent un tas de données:
mysql> select table_name, engine, table_rows, data_length, index_length from information_schema.tables WHERE tables.TABLE_SCHEMA = 'mydb' and tables.table_name in ( 'herp', 'derp', 'missing_data', 'subsidiaries');
+-------------------------+--------+------------+-------------+--------------+
| table_name | engine | table_rows | data_length | index_length |
+-------------------------+--------+------------+-------------+--------------+
| derp | MyISAM | 14085 | 1118676 | 165888 |
| herp | MyISAM | 821747 | 828106512 | 568057856 |
| missing_data | MyISAM | 1220186 | 15862418 | 29238272 |
| subsidiaries | MyISAM | 1499 | 6490308 | 103424 |
+-------------------------+--------+------------+-------------+--------------+
4 rows in set (0.00 sec)
Maintenant, lorsque j'exécute la requête ci-dessus sur ces tables, j'obtiens des temps d'exécution de plus de 1 minute alors que le système est apparemment occupé en permanence à lire des données sur le disque avec un seul thread.
Le profil d'un exemple d'exécution de requête (qui a pris 1 min 9,17 secondes dans cet exemple) ressemble à ceci:
mysql> show profile for query 1;
+--------------------------------+-----------+
| Status | Duration |
+--------------------------------+-----------+
| starting | 0.000118 |
| Waiting for query cache lock | 0.000035 |
| init | 0.000033 |
| checking query cache for query | 0.000399 |
| checking permissions | 0.000077 |
| checking permissions | 0.000030 |
| checking permissions | 0.000031 |
| checking permissions | 0.000035 |
| Opening tables | 0.000158 |
| init | 0.000294 |
| System lock | 0.000056 |
| Waiting for query cache lock | 0.000032 |
| System lock | 0.000116 |
| optimizing | 0.000063 |
| statistics | 0.001964 |
| preparing | 0.000104 |
| Sorting result | 0.000033 |
| executing | 0.000030 |
| Sending data | 2.031349 |
| optimizing | 0.000054 |
| statistics | 0.000039 |
| preparing | 0.000024 |
| executing | 0.000013 |
| Sending data | 0.000044 |
| optimizing | 0.000017 |
| statistics | 0.000021 |
| preparing | 0.000019 |
| executing | 0.000013 |
| Sending data | 21.477528 |
| executing | 0.000070 |
| Sending data | 0.000075 |
| executing | 0.000027 |
| Sending data | 45.692623 |
| end | 0.000076 |
| query end | 0.000036 |
| closing tables | 0.000109 |
| freeing items | 0.000067 |
| Waiting for query cache lock | 0.000038 |
| freeing items | 0.000080 |
| Waiting for query cache lock | 0.000044 |
| freeing items | 0.000037 |
| storing result in query cache | 0.000033 |
| logging slow query | 0.000103 |
| cleaning up | 0.000073 |
+--------------------------------+-----------+
44 rows in set, 1 warning (0.00 sec)