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)
la source
Réponses:
Permettez-moi d'abord de clarifier en confirmant que MyISAM ne fait pas d'E / S asynchrones, mais qu'InnoDB le fait et le fera par défaut à partir de MySQL 5.5. Avant 5.5, il utilisait "AIO simulé" en utilisant des threads de travail.
Je pense qu'il est également important de distinguer trois situations:
Pour (1), les E / S pourront s'exécuter en parallèle pour cela. Il y a quelques limites avec MyISAM: le verrouillage de table et un verrou global protégeant le
key_buffer
(cache d'index). InnoDB dans MySQL 5.5+ brille vraiment ici.Pour (2), cela n'est actuellement pas pris en charge. Un bon cas d'utilisation serait le partitionnement, où vous pourriez rechercher chaque table partitionnée en parallèle.
Pour (3), InnoDB a une lecture anticipée linéaire pour lire une étendue complète (groupe de 64 pages) si> 56 pages sont lues (ceci est configurable), mais il y a place pour une amélioration supplémentaire. Facebook a écrit sur l' implémentation de la tête de lecture logique dans leur branche (avec un gain de performance 10x sur les tablescans).
la source
J'espère que ce
missing_data
n'est pas MyISAM car une table MyISAM vide a généralement 1024 octets.MYI
. Une taille d'octet différente de zéro est attendue d'un MyISAM. Un octet zéro.MYI
me semble un peu effrayant.Si vous exécutez cette requête de métadonnées
et le moteur de cette table est MyISAM, vous devez le réparer.
SIDE NOTE: Si l'
engine
estNULL
, il est une vue. S'il s'agit d'une vue ou que ce n'est pas MyISAM, veuillez ignorer le reste de mon message et ajouter ces informations à la question. Si le tableau est MyISAM, lisez la suite ...Selon votre requête de métadonnées,
missing_data.MYD
c'est environ 46M.Tout d'abord, exécutez cette
Vous obtiendrez soit la description du tableau, soit un message d'erreur indiquant quelque chose comme
Si vous obtenez la description du tableau et qu'il s'agit de MyISAM, veuillez exécuter
Il recréera la table sans fragmentation et calculera de nouvelles statistiques d'index. Si cela ne fonctionne pas, essayez:
Cela devrait régénérer les pages d'index pour MyISAM.
Juste pour être sûr (si vous utilisez MySQL 5.6), exécutez-le après la réparation
Ta question
Les index de votre table peuvent ne pas être chargés en mémoire si MySQL Query Optimizer décide de ne pas utiliser. Si votre clause WHERE dicte qu'un nombre important de lignes doivent être lues à partir des index, MySQL Query Optimizer verra cela lors de la construction du plan EXPLAIN et décider d'utiliser à la place une analyse complète de la table.
Les opérations d'E / S parallèles sur une table MyISAM sont inaccessibles car non configurables.
InnoDB peut être réglé pour augmenter les performances comme ça.
la source