系列中的下一个’‘
CREATE TABLE `Alarms` ( `AlarmId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `DeviceId` BINARY(16) NOT NULL, `Code` BIGINT(20) UNSIGNED NOT NULL, `Ended` TINYINT(1) NOT NULL DEFAULT '0', `NaturalEnd` TINYINT(1) NOT NULL DEFAULT '0', `Pinned` TINYINT(1) NOT NULL DEFAULT '0', `Acknowledged` TINYINT(1) NOT NULL DEFAULT '0', `StartedAt` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', `EndedAt` TIMESTAMP NULL DEFAULT NULL, `MarkedForDeletion` TINYINT(1) NOT NULL DEFAULT '0', PRIMARY KEY (`AlarmId`), KEY `Key1` (`Ended`,`Acknowledged`), KEY `Key2` (`Pinned`), KEY `Key3` (`DeviceId`,`Pinned`), KEY `Key4` (`DeviceId`,`StartedAt`,`EndedAt`), KEY `Key5` (`DeviceId`,`Ended`,`EndedAt`), KEY `Key6` (`MarkedForDeletion`,`DeviceId`,`Acknowledged`,`Ended`,`StartedAt`), KEY `Key7` (`MarkedForDeletion`,`DeviceId`,`Ended`,`Pinned`,`EndedAt`) ) ENGINE=INNODB;
This query is quite slow when the timestamps are chosen such that many rows are matched:
SELECT (((UNIX_TIMESTAMP(`StartedAt`)) DIV 900) * 900) AS `Period_TS`, COUNT(`AlarmId`) AS `n` FROM `Alarms` WHERE `StartedAt` >= FROM_UNIXTIME(1518990000) AND `StartedAt` < FROM_UNIXTIME(1518998400) AND `DeviceId` IN ( UNHEX('00030000000000000000000000000000'), UNHEX('000300000000000000000000000181cd'), UNHEX('000300000000000000000000000e7cf6'), UNHEX('000300000000000000000000000e7cf7'), UNHEX('000300000000000000000000000f423f') ) AND `MarkedForDeletion` = FALSE GROUP BY `Period_TS` ASC;
我相信这是因为我在两个字段(DeviceId和StartedAt)上混合了范围条件。
如果是这样,我该怎么办才能解决问题?也许有什么 触发索引合并的使用?
IN在=“范围”和“范围”之间。因此,我对 问题的标题有所疑问。实际上,不可能优化两个范围。在IN加上一系列 具有优化的一些机会。
IN
基于
WHERE `StartedAt` >= FROM_UNIXTIME(1518990000) AND `StartedAt` < FROM_UNIXTIME(1518998400) AND `DeviceId` IN ( UNHEX('00030000000000000000000000000000'), UNHEX('000300000000000000000000000181cd'), UNHEX('000300000000000000000000000e7cf6'), UNHEX('000300000000000000000000000e7cf7'), UNHEX('000300000000000000000000000f423f') ) AND `MarkedForDeletion` = FALSE
I would provide 2 indexes and let the Optimizer decide which to use:
INDEX(MarkedForDeletion, StartedAt, DeviceId) INDEX(MarkedForDeletion, DeviceId, StartedAt)
一些较新的MySQL / MariaDB版本可以跳过并利用第二个索引中的所有3 列。在所有版本中,任一 索引的前2列均使其成为候选值。该选择可能受统计驱动,并且可能 (也可能不是)“正确”的选择。
既然AlarmId不能NULL,请使用模式:COUNT(*)。
AlarmId
COUNT(*)
进行更改后,我的每个索引都被“覆盖”了,从而使性能得到了进一步的提升。