Quando osservo una query particolarmente fastidiosa sulle tabelle MyISAM che richiede molto tempo per essere eseguita in diverse occasioni, ho notato che MySQL sembra esporre uno schema I / O piuttosto strano: quando si esegue una singola query e si deve fare un significativo quantità di I / O (ad es. per una scansione della tabella o quando le cache sono vuote a causa della echo 3 > /proc/sys/vm/drop_caches
necessità di caricare prima gli indici dal disco), la dimensione della coda per il dispositivo a blocchi sottostante è vicina al valore 1, con prestazioni abissali di solo 4-5 MB / 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
Mentre i 150 IOPS sono semplicemente ciò che un singolo disco in una determinata configurazione è in grado di fornire in termini di I / O casuale, il risultato mi sorprende ancora davvero poiché mi aspetterei che MySQL sia in grado di eseguire I / O asincroni per le letture e recuperare un una grande quantità di blocchi contemporaneamente invece di leggerli e valutarli uno per uno, trascurando efficacemente i guadagni di parallelizzazione disponibili nelle configurazioni RAID. Quale decisione di progettazione o opzione di configurazione è responsabile di questo? È un problema specifico della piattaforma?
Mentre l'ho provato con tabelle MyISAM di grandi dimensioni, vedo effetti simili con le stesse tabelle convertite in InnoDB (anche se non così male, la query di esempio impiega ancora 20-30 secondi con la maggior parte del tempo impiegato per leggere il disco con una lunghezza della coda di 1) dopo aver riavviato il demone mysql e quindi i pool di buffer sono vuoti. Ho anche verificato che lo stesso problema persiste su 5.6 GA e l'attuale 5.7 milestone 14 - fintanto che sto usando un singolo thread di query, MySQL sembra incapace di parallelizzare le operazioni di I / O necessarie per l'elaborazione delle query.
Come da richiesta alcuni dettagli aggiuntivi sullo scenario. Il comportamento può essere osservato con una moltitudine di tipi di query. Ne ho scelto arbitrariamente uno per ulteriori test che si legge in questo modo:
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;
So che ha un certo margine di ottimizzazione, ma ho deciso di lasciarlo per una serie di motivi e concentrarmi sulla ricerca di una spiegazione generale per l'inaspettato schema I / O che sto vedendo.
Le tabelle utilizzate contengono un sacco di dati:
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)
Ora, quando sto eseguendo la query sopra su queste tabelle, sto ottenendo tempi di esecuzione di oltre 1 minuto mentre il sistema è apparentemente continuamente impegnato a leggere i dati dal disco con un singolo thread.
Il profilo per un'esecuzione di query di esempio (che ha richiesto 1 minuto 9,17 secondi in questo esempio) è simile al seguente:
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)