Mehrere Male hatte ich solche Situation: Abfrage, die schnell gearbeitet hat, begann in einem Moment 1000-10_000 mal langsamer, wenn es keine Änderungen gab. MySQL stoppt mit dem richtigen Index und ich muss FORCE INDEX(..)
verwenden. Es passiert mit Abfragen an große Tabellen mit 10-300M Datensätze.Manchmal stoppt MySQL mit richtigen Index
MySQL: 5.6.23 (AWS RDS, db.r3.xlarge)
Es ist die letzte Ausgabe:
table1 (175M records)
CREATE TABLE `table1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`site_id` int(11) NOT NULL,
`created_at` datetime DEFAULT NULL,
`type` varchar(25) DEFAULT NULL,
...
PRIMARY KEY (`id`),
UNIQUE KEY `index_table1_on_site_id_and_..._and_type_and_...` (`site_id`,`...`,`type`,`...`),
KEY `index_table1_on_created_at_and_site_id` (`created_at`,`site_id`),
KEY `index_table1_on_site_id_and_type_and_created_at_and_...` (`site_id`,`type`,`created_at`,`...`) USING BTREE,
KEY `index_table1_on_site_and_type_and_..._and_created` (`site_id`,`type`,`..._id`,`created_at`),
) ENGINE=InnoDB AUTO_INCREMENT=... DEFAULT CHARSET=utf8
table2 (2M Datensätze)
CREATE TABLE `table2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`table1_id` int(11) NOT NULL,
...
PRIMARY KEY (`id`),
...
) ENGINE=InnoDB AUTO_INCREMENT=... DEFAULT CHARSET=utf8
Anfrage:
SELECT `table1`.* FROM `table1`
INNER JOIN `table2` ON `table2`.`table1_id` = `table1`.`id`
WHERE `table1`.`type` IN ('...', '...')
AND `table1`.`site_id` = ...
AND (table1.created_at >= '...')
AND (table1.created_at <= '...')
ORDER BY `table1`.`id` DESC LIMIT 30 offset 0;
war ~ 10-80ms jetzt> 420 sec
Anfrage mit FORCE INDEX
:
SELECT `table1`.* FROM `table1` USE INDEX (`index_table1_on_site_id_and_type_and_created_at_and_...`)
INNER JOIN `table2` ON `table2`.`table1_id` = `table1`.`id`
WHERE `table1`.`type` IN ('...', '...')
AND `table1`.`site_id` = ...
AND (table1.created_at >= '...')
AND (table1.created_at <= '...')
ORDER BY `table1`.`id` DESC LIMIT 30 offset 0;
~ 85 ms
explaine: ohne FORCE
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: table1
type: index
possible_keys: PRIMARY,index_table1_on_site_id_and_..._and_type_and_...,index_table1_on_created_at_and_site_id,index_table1_on_type,index_table1_on_site_id_and_type_and_created_at_and_...,index_table1_on_site_and_type_and_..._and_created
key: PRIMARY
key_len: 4
ref: NULL
rows: 9257179
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: table2
type: eq_ref
possible_keys: ...
key: ...
key_len: 4
ref: db.table1.id
rows: 1
Extra: Using index
mit
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: table1
type: range
possible_keys: index_table1_on_site_id_and_type_and_created_at_and_...
key: index_table1_on_site_id_and_type_and_created_at_and_...
key_len: 88
ref: NULL
rows: 499
Extra: Using index condition; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: table2
type: eq_ref
possible_keys: ...
key: ...
key_len: 4
ref: db.table1.id
rows: 1
Extra: Using index
Gibt es Lösungen, um solch unvorhersehbares Verhalten von MySQL zu vermeiden? Ich kann keine FORCE INDEX
zu allen Anfragen hinzufügen, was zu tun ist?
PS:
SELECT * FROM `table1`
INNER JOIN `table2` ON `table2`.`table1_id` = `table1`.`id`
WHERE `table1`.`site_id` = ... ;
kehrt nur 122 Datensätze
PSS: Verrückt, aber Anfrage arbeitet für eine breiteren Zeitraum schneller
AND (table1.created_at >= '2016-07-01') AND (table1.created_at <= '2016-07-07)
420 sec
AND (table1.created_at >= '2016-06-01') AND (table1.created_at <= '2016-07-07)
85 ms
mysql kann entscheiden, dass eine vollständige Tabellensuche effizienter ist als die Verwendung eines Index. Wenn es das entscheidet, dann besteht die einzige Möglichkeit darin, die Abfrage neu zu schreiben, um sie dazu zu bringen, anders zu denken oder den Index zu erzwingen. –
- es ist merkwürdig, dass MySQL 9M-Zeilen statt 499 scannen kann – Alexey
- es ist erschreckend, dass MySQL mit Indizes richtig arbeiten kann und in einem Moment entscheidet, keinen Index mehr zu verwenden – Alexey