繁体   English   中英

MySQL优化器中的奇怪查询

[英]Weird query in mysql optimizer

我在Debian 8机器上使用mysql 5.5.52,有时我们的查询速度慢(> 3s),通常花费0.1s。 我从explain命令开始,以查找正在发生的事情。

这是查询和解释信息

explain
SELECT
   `box`.`message_id` ID
 , `messages`.`tipo`
 , `messages`.`text`
 , TIME_TO_SEC(TIMEDIFF(NOW(), `messages`.`date`)) `date`
FROM (`box`)
INNER JOIN `messages` ON `messages`.`id` = `box`.`message_id`
WHERE `box`.`user_id` = '1010231' AND `box`.`deleted` = 0 
    AND `messages`.`deleted` = 0 
    AND `messages`.`date` + INTERVAL 10 MINUTE > NOW()
ORDER BY `messages`.`id` ASC LIMIT 100;


id| select_type| table  | type |  possible_keys   | key   | key_len| ref          | rows | Extra    
 1|SIMPLE      |box     |ref   |user_id,message_id|user_id|       4|const         | 2200 |Using where; Using temporary; Using filesort   
 1|SIMPLE      |messages|eq_ref|PRIMARY           |PRIMARY|       4|box.message_id|    1 |Using where

我知道临时表和文件排序是一件坏事,并且我想问题是订单键不属于查询(框)中的第一个表并将其更改为box.message_id,解释信息是

id,select_type,表,类型,可能的键,键,key_len,引用,行,额外

1 SIMPLE框索引user_id,message_id,message_id 4443在何处使用

1条简单消息eq_ref PRIMARY PRIMARY 4 box.message_id 1使用

看起来更好,但是我不明白为什么要使用message_id索引,更糟糕的是,现在查询需要1.5秒而不是最初的0.1秒

编辑:

强制查询使用user_id索引,我得到与初始查询相同的结果(0.1s),但没有临时查询

explain
SELECT
   `box`.`message_id` ID
 , `messages`.`tipo`
 , `messages`.`text`
 , TIME_TO_SEC(TIMEDIFF(NOW(), `messages`.`date`)) `date`
FROM (`box` use index(user_id) )
INNER JOIN `messages` ON `messages`.`id` = `box`.`message_id`
WHERE `box`.`user_id` = '1010231' AND `box`.`deleted` = 0 
    AND `messages`.`deleted` = 0 
    AND `messages`.`date` + INTERVAL 10 MINUTE > NOW()
ORDER BY `box`.`message_id` ASC LIMIT 100;

id| select_type| table  | type |  possible_keys   | key   | key_len| ref          | rows | Extra    
 1|SIMPLE      |box     |ref   |user_id,message_id|user_id|       4|const         | 2200 |Using where; Using filesort   
 1|SIMPLE      |messages|eq_ref|PRIMARY           |PRIMARY|       4|box.message_id|    1 |Using where

我认为跳过临时表是比初始查询更好的解决方案,下一步是按照ysth的建议检查组合索引。

计算要比较的字段值不是一个好主意。 那么您将获得一个全表扫描。 MySQL必须先检查每个ROW,然后才能检查条件。 最好在恒定条件下执行此操作。 然后MySQL可以使用索引(如果此字段中有一个)

AND messages.date + INTERVAL 10 MINUTE > NOW() 

AND messages.date  > NOW() - INTERVAL 10 MINUTE

临时和文件排序在这里不错; 它们是必需的,因为使用最佳索引(user_id)并不会自然产生按您要求的顺序排序的记录。

结合使用user_id和message_id索引可能会更好,但是最终结果也会更糟。 取决于您的确切数据。

我不清楚您是否看到更长的查询某些用户ID或相同的用户ID有时需要更长的时间。

更新:似乎有一个组合的索引并按box.user_id,box.message_id更改顺序可以解决您的问题,至少对于没有大量已删除邮件的用户而言。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM