【发布时间】:2016-12-25 16:57:08
【问题描述】:
在将 MySQL 服务器更新到 5.7 版(在 Ubuntu 16.04 LTS 下)时,我遇到了一些奇怪的问题。
Preambula:我有一些表格,里面有很多记录(约 2.5 亿)。这张表,简而言之,有这样的结构:
CREATE TABLE `device_data` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`device` int(11) DEFAULT NULL,
`data` double NOT NULL DEFAULT '0',
`utc` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`utc`,`id`),
KEY `id` (`id`),
KEY `idx_devutc` (`device`,`utc`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (`utc`)
(PARTITION y_min VALUES LESS THAN (1200000000) ENGINE = MyISAM,
PARTITION y_121 VALUES LESS THAN (1210000000) ENGINE = MyISAM,
PARTITION y_122 VALUES LESS THAN (1220000000) ENGINE = MyISAM,
...
...
...
PARTITION y_167 VALUES LESS THAN (1670000000) ENGINE = MyISAM,
PARTITION y_168 VALUES LESS THAN (1680000000) ENGINE = MyISAM,
PARTITION y_169 VALUES LESS THAN (1690000000) ENGINE = MyISAM,
PARTITION y_max VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */
那么,为什么它有如此不寻常的主键(UTC,ID)?字段 ID 是自动递增的,它可以是主键。好吧,我们需要按字段 UTC 对表进行分区,MySQL 说要这样做,字段 UTC 必须是主键或至少是主键的第一部分。我们不能同时使用UTC作为主键,因为设备每秒可以发送数次数据,所以我们必须使用这样一个奇怪的主键:(UTC,ID)。没关系。
我们还通过 (device, utc) 在此表上创建索引。为什么?因为我们需要在特定的时间段内执行特定设备检索数据的查询,例如:
SELECT `utc`, `data`
FROM `device_data`
WHERE `device` = :DeviceId AND `utc` >= :UtcFrom AND `utc` < :UtcTo
ORDER BY `utc`;
由于索引(设备,UTC),它非常快。
Ambula:将 MySQL 服务器升级到 5.7 版(从 5.0 或 5.1,我现在不确定)后,一些查询变得非常慢(2-3 分钟而不是 100-200 毫秒)。一些小调查发现了一个条件:查询速度下降,当排序顺序为后代时。
这个查询仍然很快(100 毫秒):
SELECT `utc`, `data`
FROM `device_data`
WHERE `device` = :DeviceId AND `utc` >= :UtcFrom AND `utc` < :UtcTo
ORDER BY `utc` ASC;
但是这个查询很慢(~180 秒):
SELECT `utc`, `data`
FROM `device_data`
WHERE `device` = :DeviceId AND `utc` >= :UtcFrom AND `utc` < :UtcTo
ORDER BY `utc` DESC;
当然,使用相同的参数值。 在以前版本的 MySQL 服务器下,两个查询都很快(100-200 毫秒)。
经过两天的努力,我找到了一些避免问题的技巧:
SELECT `utc`, `data`
FROM `device_data`
WHERE `device` = :DeviceId AND `utc` >= :UtcFrom AND `utc` < :UtcTo
ORDER BY -`utc` ASC;
(注意 ORDER BY 中utc 之前的减号)
这个查询也很快,在几毫秒内完成,并根据我们的需要以相反的顺序返回记录。
问题:MySQL出现这种奇怪行为的原因是什么,我该如何解决?
【问题讨论】:
-
你能用解释检查一下慢查询和快查询使用的索引吗?
-
不出意外,两者都使用 idx_devutc。更重要的是,使用 'FORCE INDEX (idx_devutc)' 修饰符的相同查询显示相同的结果。
-
额外部分没有文件排序?
-
在从 5.6 更新到 5.7.16 并引入分区后,我们看到了同样的问题。 “解释”显示相同的计划,但将 ASC 更改为 DESC 会导致响应变慢几个数量级。
-
我仍然不知道问题的原因和正确的解决方案。但是您可以使用一些技巧来避免性能泄漏。而不是“ORDER BY ID DESC”使用“ORDER BY -ID”