Problem
Eine Instanz von MySQL 5.6.20, die (meistens nur) eine Datenbank mit InnoDB-Tabellen ausführt, zeigt gelegentliche Verzögerungen für alle Aktualisierungsvorgänge für die Dauer von 1 bis 4 Minuten an, wobei alle INSERT-, UPDATE- und DELETE-Abfragen im Status "Abfrageende" verbleiben. Das ist natürlich sehr unglücklich. Das langsame MySQL-Abfrageprotokoll protokolliert selbst die trivialsten Abfragen mit wahnsinnigen Abfragezeiten, von denen Hunderte den gleichen Zeitstempel haben, der dem Zeitpunkt entspricht, zu dem der Stillstand behoben wurde:
# Query_time: 101.743589 Lock_time: 0.000437 Rows_sent: 0 Rows_examined: 0
SET timestamp=1409573952;
INSERT INTO sessions (redirect_login2, data, hostname, fk_users_primary, fk_users, id_sessions, timestamp) VALUES (NULL, NULL, '192.168.10.151', NULL, 'anonymous', '64ef367018099de4d4183ffa3bc0848a', '1409573850');
Die Gerätestatistiken werden erhöht angezeigt, obwohl in diesem Zeitraum keine übermäßige E / A-Belastung aufgetreten ist (in diesem Fall wurden die Aktualisierungen gemäß den Zeitstempeln aus der obigen Anweisung zwischen 14:17:30 und 14:19:12 blockiert):
# sar -d
[...]
02:15:01 PM DEV tps rd_sec/s wr_sec/s avgrq-sz avgqu-sz await svctm %util
02:16:01 PM dev8-0 41.53 207.43 1227.51 34.55 0.34 8.28 3.89 16.15
02:17:01 PM dev8-0 59.41 137.71 2240.32 40.02 0.39 6.53 4.04 24.00
02:18:01 PM dev8-0 122.08 2816.99 1633.44 36.45 3.84 31.46 1.21 2.88
02:19:01 PM dev8-0 253.29 5559.84 3888.03 37.30 6.61 26.08 1.85 6.73
02:20:01 PM dev8-0 101.74 1391.92 2786.41 41.07 1.69 16.57 3.55 36.17
[...]
# sar
[...]
02:15:01 PM CPU %user %nice %system %iowait %steal %idle
02:16:01 PM all 15.99 0.00 12.49 2.08 0.00 69.44
02:17:01 PM all 13.67 0.00 9.45 3.15 0.00 73.73
02:18:01 PM all 10.64 0.00 6.26 11.65 0.00 71.45
02:19:01 PM all 3.83 0.00 2.42 24.84 0.00 68.91
02:20:01 PM all 20.95 0.00 15.14 6.83 0.00 57.07
In den meisten Fällen stelle ich im langsamen mysql-Protokoll fest, dass die älteste Abfrage ein INSERT in eine große Tabelle (~ 10 M Zeilen) mit einem VARCHAR-Primärschlüssel und einem Volltextsuchindex ist:
CREATE TABLE `files` (
`id_files` varchar(32) NOT NULL DEFAULT '',
`filename` varchar(100) NOT NULL DEFAULT '',
`content` text,
PRIMARY KEY (`id_files`),
KEY `filename` (`filename`),
FULLTEXT KEY `content` (`content`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Weitere Untersuchungen (zB SHOW ENGINE INNODB STATUS) haben gezeigt, dass es sich in der Tat immer um eine Aktualisierung einer Tabelle unter Verwendung von Volltextindizes handelt, die den Stillstand verursacht. Der entsprechende TRANSACTIONS-Abschnitt von "SHOW ENGINE INNODB STATUS" enthält Einträge wie diese beiden für die ältesten ausgeführten Transaktionen:
---TRANSACTION 162269409, ACTIVE 122 sec doing SYNC index
6 lock struct(s), heap size 1184, 0 row lock(s), undo log entries 19942
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_1" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_2" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_3" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_4" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_5" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_6" trx id 162269409 lock mode IX
---TRANSACTION 162269408, ACTIVE (PREPARED) 122 sec committing
mysql tables in use 1, locked 1
1 lock struct(s), heap size 360, 0 row lock(s), undo log entries 1
MySQL thread id 165998, OS thread handle 0x7fe0e239c700, query id 91208956 192.168.10.153 root query end
INSERT INTO files (id_files, filename, content) VALUES ('f19e63340fad44841580c0371bc51434', '1237716_File_70380a686effd6b66592bb5eeb3d9b06.doc', '[...]
TABLE LOCK table `vw`.`files` trx id 162269408 lock mode IX
Es wird also eine schwere Volltextindex-Aktion ausgeführt ( doing SYNC index
) , mit der ALLE FOLGENDEN Aktualisierungen für JEDE Tabelle gestoppt werden.
Aus den Protokollen geht hervor, dass die undo log entries
Zahl doing SYNC index
bei ~ 150 / s voranschreitet, bis sie 20.000 erreicht. Zu diesem Zeitpunkt ist der Vorgang abgeschlossen.
Die FTS-Größe dieser Tabelle ist beeindruckend:
# du -c FTS_000000000000224a_00000000000036b9_*
614404 FTS_000000000000224a_00000000000036b9_INDEX_1.ibd
2478084 FTS_000000000000224a_00000000000036b9_INDEX_2.ibd
1576964 FTS_000000000000224a_00000000000036b9_INDEX_3.ibd
1630212 FTS_000000000000224a_00000000000036b9_INDEX_4.ibd
1978372 FTS_000000000000224a_00000000000036b9_INDEX_5.ibd
1159172 FTS_000000000000224a_00000000000036b9_INDEX_6.ibd
9437208 total
Obwohl das Problem auch von Tabellen mit einer wesentlich geringeren FTS-Datengröße wie der folgenden ausgelöst wird:
# du -c FTS_0000000000002467_0000000000003a21_INDEX*
49156 FTS_0000000000002467_0000000000003a21_INDEX_1.ibd
225284 FTS_0000000000002467_0000000000003a21_INDEX_2.ibd
147460 FTS_0000000000002467_0000000000003a21_INDEX_3.ibd
135172 FTS_0000000000002467_0000000000003a21_INDEX_4.ibd
155652 FTS_0000000000002467_0000000000003a21_INDEX_5.ibd
106500 FTS_0000000000002467_0000000000003a21_INDEX_6.ibd
819224 total
Auch in diesen Fällen ist die Standzeit in etwa gleich. Ich habe einen Bug auf bugs.mysql.com geöffnet, damit die Entwickler sich darum kümmern können.
Die Art der Stalls hat mich zunächst verdächtigt, dass das Löschen von Protokollen der Schuldige ist. In diesem Percona-Artikel zu Problemen mit der Leistung beim Löschen von Protokollen in MySQL 5.5 werden sehr ähnliche Symptome beschrieben sind ebenfalls vom Stall betroffen, daher scheint dies kein reines InnoDB-Problem zu sein.
Trotzdem habe ich beschlossen, alle 10 Sekunden die Werte der Log sequence number
und Pages flushed up to
von den Ausgaben des Abschnitts "LOG" zu verfolgen SHOW ENGINE INNODB STATUS
. Es sieht tatsächlich so aus, als ob während des Stalls eine Spülaktivität stattfindet, da die Spreizung zwischen den beiden Werten abnimmt:
Mon Sep 1 14:17:08 CEST 2014 LSN: 263992263703, Pages flushed: 263973405075, Difference: 18416 K
Mon Sep 1 14:17:19 CEST 2014 LSN: 263992826715, Pages flushed: 263973811282, Difference: 18569 K
Mon Sep 1 14:17:29 CEST 2014 LSN: 263993160647, Pages flushed: 263974544320, Difference: 18180 K
Mon Sep 1 14:17:39 CEST 2014 LSN: 263993539171, Pages flushed: 263974784191, Difference: 18315 K
Mon Sep 1 14:17:49 CEST 2014 LSN: 263993785507, Pages flushed: 263975990474, Difference: 17377 K
Mon Sep 1 14:17:59 CEST 2014 LSN: 263994298172, Pages flushed: 263976855227, Difference: 17034 K
Mon Sep 1 14:18:09 CEST 2014 LSN: 263994670794, Pages flushed: 263978062309, Difference: 16219 K
Mon Sep 1 14:18:19 CEST 2014 LSN: 263995014722, Pages flushed: 263983319652, Difference: 11420 K
Mon Sep 1 14:18:30 CEST 2014 LSN: 263995404674, Pages flushed: 263986138726, Difference: 9048 K
Mon Sep 1 14:18:40 CEST 2014 LSN: 263995718244, Pages flushed: 263988558036, Difference: 6992 K
Mon Sep 1 14:18:50 CEST 2014 LSN: 263996129424, Pages flushed: 263988808179, Difference: 7149 K
Mon Sep 1 14:19:00 CEST 2014 LSN: 263996517064, Pages flushed: 263992009344, Difference: 4402 K
Mon Sep 1 14:19:11 CEST 2014 LSN: 263996979188, Pages flushed: 263993364509, Difference: 3529 K
Mon Sep 1 14:19:21 CEST 2014 LSN: 263998880477, Pages flushed: 263993558842, Difference: 5196 K
Mon Sep 1 14:19:31 CEST 2014 LSN: 264001013381, Pages flushed: 263993568285, Difference: 7270 K
Mon Sep 1 14:19:41 CEST 2014 LSN: 264001933489, Pages flushed: 263993578961, Difference: 8158 K
Mon Sep 1 14:19:51 CEST 2014 LSN: 264004225438, Pages flushed: 263993585459, Difference: 10390 K
Und um 14:19:11 hat der Spread sein Minimum erreicht, so dass die Spülaktivität hier aufgehört zu haben scheint und nur mit dem Ende des Stalls zusammenfällt. Aufgrund dieser Punkte habe ich das Leeren des InnoDB-Protokolls als Ursache verworfen:
- Damit der Löschvorgang alle Aktualisierungen der Datenbank blockiert, muss er "synchron" sein, dh, 7/8 des Protokollspeicherplatzes müssen belegt sein
- es würde eine "asynchrone" Spülphase ab
innodb_max_dirty_pages_pct
Füllstand vorausgehen - was ich nicht sehe - Die LSNs steigen auch während des Stalls weiter an, sodass die Protokollaktivität nicht vollständig aufhört
- Auch die INSERTs der MyISAM-Tabelle sind betroffen
- Der page_cleaner-Thread für das adaptive Leeren scheint seine Arbeit zu erledigen und die Protokolle zu leeren, ohne dass DML-Abfragen beendet werden:
(Zahlen sind ([Log Sequence Number] - [Pages flushed up to]) / 1024
von SHOW ENGINE INNODB STATUS
)
Das Problem scheint durch das Einstellen etwas gelindert zu werden innodb_adaptive_flushing_lwm=1
, wodurch die Bereinigung der Seite gezwungen wird, mehr Arbeit als zuvor zu leisten.
Der error.log
hat keine Einträge, die mit den Ständen übereinstimmen. SHOW INNODB STATUS
Auszüge nach ca. 24 Betriebsstunden sehen so aus:
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 789330
OS WAIT ARRAY INFO: signal count 1424848
Mutex spin waits 269678, rounds 3114657, OS waits 65965
RW-shared spins 941620, rounds 20437223, OS waits 442474
RW-excl spins 451007, rounds 13254440, OS waits 215151
Spin rounds per wait: 11.55 mutex, 21.70 RW-shared, 29.39 RW-excl
------------------------
LATEST DETECTED DEADLOCK
------------------------
2014-09-03 10:33:55 7fe0e2e44700
[...]
--------
FILE I/O
--------
[...]
932635 OS file reads, 2117126 OS file writes, 1193633 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 17.00 writes/s, 1.20 fsyncs/s
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Main thread process no. 54745, id 140604272338688, state: sleeping
Number of rows inserted 528904, updated 1596758, deleted 99860, read 3325217158
5.40 inserts/s, 10.40 updates/s, 0.00 deletes/s, 122969.21 reads/s
Ja, die Datenbank hat Deadlocks, aber sie sind sehr selten (das "Neueste" wurde ungefähr 11 Stunden vor dem Lesen der Statistiken behandelt).
Ich habe versucht, die Werte des Abschnitts "SEMAPHORES" über einen bestimmten Zeitraum zu verfolgen, insbesondere in einer normalen Betriebssituation und während eines Stillstands (ich habe ein kleines Skript geschrieben, das die Prozessliste des MySQL-Servers überprüft und für den Fall einige Diagnosebefehle in eine Protokollausgabe eingibt von einem offensichtlichen Stall). Da die Zahlen über verschiedene Zeiträume hinweg aufgenommen wurden, habe ich die Ergebnisse auf Ereignisse / Sekunde normalisiert:
normal stall
1h avg 1m avg
OS WAIT ARRAY INFO:
reservation count 5,74 1,00
signal count 24,43 3,17
Mutex spin waits 1,32 5,67
rounds 8,33 25,85
OS waits 0,16 0,43
RW-shared spins 9,52 0,76
rounds 140,73 13,39
OS waits 2,60 0,27
RW-excl spins 6,36 1,08
rounds 178,42 16,51
OS waits 2,38 0,20
Ich bin mir nicht ganz sicher, was ich hier sehe. Die meisten Zahlen sind um eine Größenordnung gesunken - wahrscheinlich aufgrund eingestellter Aktualisierungsvorgänge, "Mutex Spin Waits" und "Mutex Spin Rounds" haben sich jedoch beide um den Faktor 4 erhöht.
Um dies weiter zu untersuchen, enthält die Liste von mutexes ( SHOW ENGINE INNODB MUTEX
) ~ 480 mutex-Einträge, die sowohl im normalen Betrieb als auch während eines Stillstands aufgelistet sind. Ich habe aktiviert, um innodb_status_output_locks
zu sehen, ob es mir mehr Details geben wird.
Konfigurationsvariablen
(Ich habe mit den meisten von ihnen ohne bestimmten Erfolg gebastelt):
mysql> show global variables where variable_name like 'innodb_adaptive_flush%';
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| innodb_adaptive_flushing | ON |
| innodb_adaptive_flushing_lwm | 1 |
+------------------------------+-------+
mysql> show global variables where variable_name like 'innodb_max_dirty_pages_pct%';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_max_dirty_pages_pct | 50 |
| innodb_max_dirty_pages_pct_lwm | 10 |
+--------------------------------+-------+
mysql> show global variables where variable_name like 'innodb_log_%';
+-----------------------------+-----------+
| Variable_name | Value |
+-----------------------------+-----------+
| innodb_log_buffer_size | 8388608 |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 268435456 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
+-----------------------------+-----------+
mysql> show global variables where variable_name like 'innodb_double%';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| innodb_doublewrite | ON |
+--------------------+-------+
mysql> show global variables where variable_name like 'innodb_buffer_pool%';
+-------------------------------------+----------------+
| Variable_name | Value |
+-------------------------------------+----------------+
| innodb_buffer_pool_dump_at_shutdown | OFF |
| innodb_buffer_pool_dump_now | OFF |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | OFF |
| innodb_buffer_pool_load_at_startup | OFF |
| innodb_buffer_pool_load_now | OFF |
| innodb_buffer_pool_size | 29360128000 |
+-------------------------------------+----------------+
mysql> show global variables where variable_name like 'innodb_io_capacity%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| innodb_io_capacity | 200 |
| innodb_io_capacity_max | 2000 |
+------------------------+-------+
mysql> show global variables where variable_name like 'innodb_lru_scan_depth%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_lru_scan_depth | 1024 |
+-----------------------+-------+
Sachen schon ausprobiert
- Deaktivieren des Abfragecaches durch
SET GLOBAL query_cache_size=0
- Erhöhung
innodb_log_buffer_size
auf 128M - das Spiel mit
innodb_adaptive_flushing
,innodb_max_dirty_pages_pct
und die jeweiligen_lwm
Werte (sie auf die Standardwerte gesetzt wurden , um meine Änderungen vor) - ansteigend
innodb_io_capacity
(2000) undinnodb_io_capacity_max
(4000) - Rahmen
innodb_flush_log_at_trx_commit = 2
- Laufen mit innodb_flush_method = O_DIRECT (ja, wir verwenden ein SAN mit einem persistenten Schreibcache)
- Setzen Sie den / sys / block / sda / queue / scheduler auf
noop
oderdeadline