2016-07-07 7 views
0

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

+0

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. –

+0

- es ist merkwürdig, dass MySQL 9M-Zeilen statt 499 scannen kann – Alexey

+0

- es ist erschreckend, dass MySQL mit Indizes richtig arbeiten kann und in einem Moment entscheidet, keinen Index mehr zu verwenden – Alexey

Antwort

0

Wenn sich die Tabellen geändert haben, können Sie versuchen, ANALYZE TABLE (http://dev.mysql.com/doc/refman/5.7/en/analyze-table.html) auszuführen, um die Statistiken synchron zu aktualisieren. InnoDB persists optimizer stats, die einige limitations hat.

Basierend auf den Datumsbereichen, frage ich mich auch, wenn es so schnell einfach sein würde, wenn Sie

tun
AND (table1.created_at >= '2016-06-01') AND (table1.created_at <= '2016-06-07)' 

unter der Annahme, dass die älteren Daten stabile Statistiken haben, und es ist nicht die Größe, die den Unterschied macht.

+0

Tabellen wurden nicht geändert – Alexey

+0

Ich laufe 'ANALYZE TABLE', aber es hat nicht geholfen – Alexey

+0

Interessant, Abfrage mit' created_at> = '2016-06-01' UND created_at <= '2016-06-07'' und 'created_at > = '2016-07-01' und created_at <= '2016-07-07' arbeitet langsam, aber mit 'created_at> = '2016-06-01' und created_at <= '2016-07-07'' funktioniert schnell – Alexey