Problema
Un'istanza di MySQL 5.6.20 che esegue (principalmente solo) un database con tabelle InnoDB mostra bancarelle occasionali per tutte le operazioni di aggiornamento per la durata di 1-4 minuti con tutte le query INSERT, UPDATE e DELETE che rimangono nello stato "Fine query". Questo ovviamente è molto sfortunato. Il log delle query lente di MySQL registra anche le query più banali con tempi di query folli, centinaia di loro con lo stesso timestamp corrispondente al punto nel tempo in cui lo stallo è stato risolto:
# 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');
E le statistiche del dispositivo stanno mostrando un aumento, anche se non un carico I / O eccessivo in questo intervallo di tempo (in questo caso gli aggiornamenti erano in stallo 14:17:30 - 14:19:12 secondo i timestamp della dichiarazione sopra):
# 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
Più spesso, noto nel registro lento mysql che lo stallo della query più vecchio è un INSERT in una tabella di grandi dimensioni (~ 10 M righe) con una chiave primaria VARCHAR e un indice di ricerca full-text:
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
Ulteriori ricerche (es. SHOW ENGINE INNODB STATUS) hanno dimostrato che si tratta sempre di un aggiornamento di una tabella che utilizza indici full-text che causano lo stallo. La rispettiva sezione TRANSAZIONI di "SHOW ENGINE INNODB STATUS" contiene voci come queste due per le transazioni in esecuzione più vecchie:
---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
Quindi c'è qualche pesante azione sull'indice full-text in corso lì ( doing SYNC index
) che ferma TUTTI gli aggiornamenti SUCCESSIVI a QUALSIASI tabella.
Dai registri sembra un po 'come se il undo log entries
numero doing SYNC index
avanzi a ~ 150 / s fino a raggiungere i 20.000, a quel punto l'operazione viene eseguita.
La dimensione FTS di questa tabella specifica è piuttosto impressionante:
# 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
sebbene il problema sia anche innescato da tabelle con dimensioni di dati FTS significativamente meno massicce come questa:
# 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
Anche in questi casi il tempo della stalla è più o meno lo stesso. Ho aperto un bug su bugs.mysql.com in modo che gli sviluppatori possano esaminarlo.
La natura delle bancarelle mi ha inizialmente fatto sospettare che l'attività di lavaggio del registro sia il colpevole e questo articolo di Percona su problemi di prestazioni di lavaggio del registro con MySQL 5.5 descrive sintomi molto simili, ma ulteriori occorrenze hanno dimostrato che le operazioni INSERT nella singola tabella MyISAM in questo database sono interessati anche dallo stallo, quindi questo non sembra un problema solo di InnoDB.
Tuttavia, ho deciso di tenere traccia dei valori di Log sequence number
e Pages flushed up to
dalla sezione "LOG" delle uscite SHOW ENGINE INNODB STATUS
ogni 10 secondi. Sembra infatti che durante lo stallo sia in corso l'attività di lavaggio mentre la diffusione tra i due valori sta diminuendo:
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
E alle 14:19:11 la diffusione ha raggiunto il suo minimo, quindi l'attività di irrigazione sembra essere cessata qui, proprio in coincidenza con la fine della bancarella. Ma questi punti mi hanno fatto respingere il flush del log di InnoDB come causa:
- affinché l'operazione di scaricamento blocchi tutti gli aggiornamenti del database, deve essere "sincrono", il che significa che è necessario occupare 7/8 dello spazio del registro
- sarebbe preceduto da una fase di lavaggio "asincrona" a partire dal
innodb_max_dirty_pages_pct
livello di riempimento - che non vedo - gli LSN continuano ad aumentare anche durante lo stallo, quindi l'attività di registro non si interrompe completamente
- Anche gli INSERT della tabella MyISAM sono interessati
- il thread page_cleaner per il flushing adattivo sembra fare il suo lavoro e svuotare i log senza causare l'interruzione delle query DML:
(i numeri provengono ([Log Sequence Number] - [Pages flushed up to]) / 1024
da SHOW ENGINE INNODB STATUS
)
Il problema sembra in qualche modo alleviato dall'impostazione innodb_adaptive_flushing_lwm=1
, costringendo il pulitore della pagina a fare più lavoro di prima.
Non error.log
ci sono voci che coincidono con le bancarelle. SHOW INNODB STATUS
gli estratti dopo circa 24 ore di funzionamento si presentano così:
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
Quindi, sì, il database ha deadlock, ma sono molto rari (l'ultimo è stato gestito circa 11 ore prima della lettura delle statistiche).
Ho provato a tracciare i valori della sezione "SEMAPHORES" per un periodo di tempo, specialmente in una situazione di normale funzionamento e durante uno stallo (ho scritto un piccolo script che controllava l'elenco dei processi del server MySQL ed eseguiva un paio di comandi diagnostici in un output del registro nel caso in cui di una stalla ovvia). Dato che i numeri sono stati rilevati in diversi intervalli di tempo, ho normalizzato i risultati in eventi / secondo:
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
Non sono del tutto sicuro di quello che vedo qui. La maggior parte dei numeri è diminuita di un ordine di grandezza - probabilmente a causa di cessate operazioni di aggiornamento, "Mutex spin wait" e "Mutex spin rounds" sono entrambi aumentati del fattore 4.
Analizzando ulteriormente questo, l'elenco dei mutex ( SHOW ENGINE INNODB MUTEX
) ha ~ 480 voci mutex elencate sia durante il normale funzionamento che durante uno stallo. Ho abilitato innodb_status_output_locks
a vedere se mi darà maggiori dettagli.
Variabili di configurazione
(Ho armeggiato con la maggior parte di loro senza un successo definito):
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 |
+-----------------------+-------+
Le cose hanno già provato
- disabilitando la cache delle query di
SET GLOBAL query_cache_size=0
- aumentando
innodb_log_buffer_size
a 128M - giocare con
innodb_adaptive_flushing
,innodb_max_dirty_pages_pct
e le rispettive_lwm
valori (che sono stati fissati per default prima del mio modifiche) - crescente
innodb_io_capacity
(2000) einnodb_io_capacity_max
(4000) - ambientazione
innodb_flush_log_at_trx_commit = 2
- in esecuzione con innodb_flush_method = O_DIRECT (sì, usiamo una SAN con una cache di scrittura persistente)
- impostando / sys / block / sda / queue / scheduler su
noop
odeadline