我正在诊断一个间歇性的慢查询,并在MySQL中发现了一个我无法解释的奇怪行为。它只针对一个特定情况选择不同的非最佳关键策略,仅在执行LIMIT 1
时。
表格(为简洁起见,删除了一些未引用的数据列)
CREATE TABLE `ch_log` (
`cl_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`cl_unit_id` INT(11) NOT NULL DEFAULT '0',
`cl_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`cl_type` CHAR(1) NOT NULL DEFAULT '',
`cl_data` TEXT NOT NULL,
`cl_event` VARCHAR(255) NULL DEFAULT NULL,
`cl_timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`cl_record_status` CHAR(1) NOT NULL DEFAULT 'a',
PRIMARY KEY (`cl_id`),
INDEX `cl_type` (`cl_type`),
INDEX `cl_date` (`cl_date`),
INDEX `cl_event` (`cl_event`),
INDEX `cl_unit_id` (`cl_unit_id`),
INDEX `log_type_unit_id` (`cl_unit_id`, `cl_type`),
INDEX `unique_user` (`cl_user_number`, `cl_unit_id`)
)
ENGINE=InnoDB
AUTO_INCREMENT=419582094;
这是一个查询,仅针对某个特定cl_unit_id
运行缓慢:
EXPLAIN
SELECT *
FROM `ch_log`
WHERE `ch_log_type` ='I' and ch_log_event = 'G'
AND cl_unit_id=1234
ORDER BY cl_date DESC
LIMIT 1;
id|select_type|table |type |possible_keys |key |key_len|ref|rows|Extra
1 |SIMPLE |ch_log|index|cl_type,cl_event,cl_unit_id,log_type_unit_id|cl_date|8 |\N |5295|Using where
对于cl_unit_id
的所有其他值,它使用更快的log_type_unit_id
键。
id|select_type|table |type|possible_keys |key |key_len|ref |rows|Extra
1 |SIMPLE |ch_log|ref |ch_log_type,ch_log_event,ch_log_unit_id,log_type_unit_id|log_type_unit_id|5 |const,const|3804|Using where; Using filesort
我看不出这个'单位'的数据有什么奇怪之处:</ p>
一般信息
我尝试过的事情,并且可以通过以下方式解决问题:
删除LIMIT 1
- 查询以毫秒为单位运行并返回数据。
更改为LIMIT 2
或其他组合,例如2,3 - 以毫秒为单位运行。
添加索引提示 - 解决它:
FROM `ch_log` USE INDEX (log_type_unit_id)
但是......我不想将其硬编码到应用程序中。
在主键上添加第二个订单也“解决”它:
ORDER BY cl_id, cl_date DESC
给出解释:
id|select_type|table |type|possible_keys |key |key_len|ref |rows|Extra
1 |SIMPLE |ch_log|ref |ch_log_type,ch_log_event,ch_log_unit_id,log_type_unit_id|log_type_unit_id|5 |const,const|6870|Using where
与提示的类型稍有不同,检查了更多记录(6,000)但仍然以10毫秒运行。
我再次可以这样做,但我不喜欢使用我不理解的副作用。
所以我认为我的主要问题是:
a)为什么只发生在LIMIT 1
?
b)数据本身如何影响关键策略呢?数据的哪个方面,看起来像索引中的数量和传播似乎是典型的。
答案 0 :(得分:10)
Mysql将选择一个解释计划并使用不同的索引,具体取决于它认为在统计上是最佳选择。对于你的所有第一个问题,这就是答案:
LIMIT 1
- 查询以毫秒为单位运行并返回数据。
和 - &gt;是的,检查一下,解释计划是好的LIMIT 2
或其他组合,例如2,3 - 以毫秒为单位运行。 - &GT;同样适用。优化器选择一个不同的索引,因为突然,预期的块读取变成了LIMIT 1
的两倍(这只是一种可能性)现在,这只回答了一半的问题。
a)为什么它只发生在LIMIT 1?
实际上,这不仅仅是因为LIMIT 1
,而是因为
ORDER BY DESC
条款。尝试使用ORDER BY ... ASC
,您也可能会看到改进。这种现象非常明显。请read on。
其中一个可接受的解决方案(文章自下而上)是强制索引,就像你做的那样。是的,有时,这是合理的。否则,很久以前就会彻底消除这种暗示。机器人不可能永远完美: - )
b)数据本身如何影响关键策略?什么 数据的一个方面,看作索引中的数量和传播 看起来很典型。
你说过,传播通常是乱搞。不仅优化器可能只是通过准确的统计数据做出错误的决定,但它也可能完全关闭只是因为表{delta} {...}