2016-07-07 55 views
0

有好几次我遇到过这样的情况:快速工作的查询开始工作,在没有变化的一刻慢1000-10000倍。 MySQL停止使用正确的索引,我必须使用FORCE INDEX(..)。它发生在查询具有10-300M记录的大表时。有时MySQL会停止使用正确的索引

MySQL的:23年6月5日(AWS RDS,db.r3.xlarge)

还有最后一个问题:

表1(175M记录)

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 

表2(2M记录)

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 

请求:

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; 

为〜10-80ms 现在> 420秒

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毫秒

EXPLAINE: 而不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 

FORCE

*************************** 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 

是否有解决方案来避免这种不可预测的MySQL行为?我无法将FORCE INDEX添加到所有请求中,该怎么办?

PS:

SELECT * FROM `table1` 
INNER JOIN `table2` ON `table2`.`table1_id` = `table1`.`id` 
WHERE `table1`.`site_id` = ... ; 

返回刚刚122记录

PSS:疯狂,但请求工作更快更广泛的时间段

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

420秒

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

85ms

+0

mysql可以决定全表扫描比使用索引更有效。如果它决定,那么你只有选择重写查询来欺骗它,否则强制索引。 –

+0

- 奇怪的是,MySQL可能更喜欢扫描9M行而不是499 – Alexey

+0

- 很可怕的是,MySQL可以正确使用索引,并且一时决定不再使用索引 – Alexey

回答

0

如果表格已更改,您可以尝试运行ANALYZE TABLEhttp://dev.mysql.com/doc/refman/5.7/en/analyze-table.html)以同步更新统计信息。 InnoDB persists optimizer stats其中有一些limitations

基础上的日期范围,我也想知道,如果你没有

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

假定旧数据有更稳定的统计这将是一样快,这不是使其中的差别大小。

+0

表没有被改变 – Alexey

+0

我运行'ANALYZE TABLE',但它没有帮助 – Alexey

+0

有趣的是,查询与'created_at> ='2016-06-01'AND created_at <='2016-06-07''和'created_at > ='2016-07-01'AND created_at <='2016-07-07'工作缓慢,但是'created_at> ='2016-06-01'AND created_at <='2016-07-07''作品快速 – Alexey