Bei der Betrachtung einer besonders ärgerlichen Abfrage über MyISAM-Tabellen, deren Ausführung mehrmals lange dauert, habe ich festgestellt, dass MySQL ein ziemlich seltsames E / A-Muster aufzudecken scheint: Wenn eine einzelne Abfrage ausgeführt wird und eine signifikante Abfrage ausgeführt werden muss E / A-Menge (z. B. für einen Tabellenscan oder wenn die Caches leer sind, echo 3 > /proc/sys/vm/drop_caches
sodass die Indizes zuerst von der Festplatte geladen werden müssen), liegt die Warteschlangengröße für das zugrunde liegende Blockgerät nahe dem Wert 1 mit einer miserablen Leistung von nur 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
Während die 150 IOPS einfach das sind, was eine einzelne Festplatte in der angegebenen Konfiguration in Bezug auf zufällige E / A liefern kann, überrascht mich das Ergebnis immer noch sehr , da ich erwarten würde, dass MySQL asynchrone E / A für Lese- und Abrufvorgänge ausführen kann Eine große Anzahl von Blöcken gleichzeitig, anstatt sie einzeln zu lesen und auszuwerten, wodurch die in RAID-Konfigurationen verfügbaren Parallelisierungsgewinne effektiv vernachlässigt werden. Welche Entwurfsentscheidung oder Konfigurationsoption ist dafür verantwortlich? Ist das ein plattformspezifisches Problem?
Während ich dies mit großen MyISAM-Tabellen getestet habe, sehe ich ähnliche Effekte mit denselben Tabellen, die in InnoDB konvertiert wurden (obwohl nicht so schlecht, dauert die Beispielabfrage immer noch 20 bis 30 Sekunden, wobei die meiste Zeit für das Lesen der Festplatte aufgewendet wird eine Warteschlangenlänge von 1) nach dem Neustart des MySQL-Daemons und daher sind die Pufferpools leer. Ich habe auch überprüft, dass das gleiche Problem bei 5.6 GA und dem aktuellen 5.7-Meilenstein 14 weiterhin besteht. Solange ich einen einzelnen Abfragethread verwende, scheint MySQL nicht in der Lage zu sein, die für die Abfrageverarbeitung erforderlichen E / A-Vorgänge zu parallelisieren.
Auf Anfrage einige zusätzliche Details zum Szenario. Das Verhalten kann mit einer Vielzahl von Abfragetypen beobachtet werden. Ich habe willkürlich einen für weitere Tests ausgewählt, der ungefähr so lautet:
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;
Ich weiß, dass es Raum für Optimierungen gibt, aber ich habe mich aus mehreren Gründen entschlossen, dies zu belassen und mich darauf zu konzentrieren, eine allgemeine Erklärung für das unerwartete E / A-Muster zu finden, das ich sehe.
Die verwendeten Tabellen enthalten eine Reihe von Daten:
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)
Wenn ich jetzt die obige Abfrage über diese Tabellen ausführe, erhalte ich Ausführungszeiten von über 1 Minute, während das System anscheinend ständig damit beschäftigt ist, Daten von der Festplatte mit einem einzelnen Thread zu lesen.
Das Profil für eine Beispielabfrageausführung (die in diesem Beispiel 1 Minute und 9,17 Sekunden dauerte) sieht folgendermaßen aus:
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)
quelle
Antworten:
Lassen Sie mich zunächst klarstellen, dass MyISAM keine asynchronen E / A-Vorgänge ausführt, InnoDB dies jedoch standardmäßig in MySQL 5.5 tut und tun wird. Vor 5.5 wurde "simuliertes AIO" unter Verwendung von Arbeitsthreads verwendet.
Ich denke, es ist auch wichtig, zwischen drei Situationen zu unterscheiden:
Für (1) E / A kann hierfür parallel ausgeführt werden. Bei MyISAM gibt es einige Einschränkungen: Tabellensperre und eine globale Sperre, die den
key_buffer
(Index-Cache) schützt . InnoDB in MySQL 5.5+ glänzt hier wirklich.Für (2) wird dies derzeit nicht unterstützt. Ein guter Anwendungsfall wäre die Partitionierung, bei der Sie jede partitionierte Tabelle parallel durchsuchen können.
Für (3) InnoDB verfügt über eine lineare Vorauslesung, um einen vollständigen Umfang (Gruppe von 64 Seiten) zu lesen, wenn> 56 Seiten gelesen werden (dies ist konfigurierbar), aber es gibt Raum für weitere Verbesserungen. Facebook hat über die Implementierung von Logical-Readhead in seinem Zweig geschrieben (mit einem 10-fachen Leistungsgewinn bei Tischscans).
quelle
Ich hoffe, es
missing_data
ist nicht MyISAM, da eine leere MyISAM-Tabelle normalerweise 1024 Byte hat.MYI
. Von einem MyISAM wird eine Bytegröße ungleich Null erwartet. Ein Null-Byte.MYI
klingt für mich etwas gruselig.Wenn Sie diese Metadatenabfrage ausführen
und die Engine dieser Tabelle ist MyISAM. Sie müssen sie reparieren.
SEITLICHER HINWEIS: Wenn dies der Fall
engine
istNULL
, handelt es sich um eine Ansicht. Wenn es sich um eine Ansicht handelt oder nicht um MyISAM, ignorieren Sie bitte den Rest meines Beitrags und fügen Sie diese Informationen der Frage hinzu. Wenn die Tabelle MyISAM ist, lesen Sie weiter ...Laut Ihrer Metadatenabfrage sind
missing_data.MYD
es ungefähr 46 Millionen.Führen Sie dies zuerst aus
Sie erhalten entweder die Tabellenbeschreibung oder eine Fehlermeldung mit der Aufschrift "Ähnliches"
Wenn Sie die Tabellenbeschreibung erhalten und es sich um MyISAM handelt, führen Sie es bitte aus
Die Tabelle wird ohne Fragmentierung neu erstellt und neue Indexstatistiken berechnet. Wenn das nicht funktioniert, versuchen Sie:
Dadurch sollten die Indexseiten für MyISAM neu generiert werden.
Führen Sie dies nach der Reparatur aus Sicherheitsgründen aus (wenn Sie MySQL 5.6 verwenden)
Ihre Frage
Die Indizes Ihrer Tabelle werden möglicherweise nicht in den Speicher geladen, wenn das MySQL Query Optimizer keine Verwendung verwendet. Wenn Ihre WHERE-Klausel vorschreibt, dass eine erhebliche Anzahl von Zeilen aus den Indizes gelesen werden muss, erkennt MySQL Query Optimizer dies beim Erstellen des EXPLAIN-Plans und entscheidet sich stattdessen für einen vollständigen Tabellenscan.
Parallele E / A-Operationen für eine MyISAM-Tabelle sind nicht erreichbar, da sie nicht konfigurierbar sind.
InnoDB kann so eingestellt werden, dass die Leistung gesteigert wird.
quelle