LIMIT和未索引的列似乎阻止了ORDER BY优化

时间:2017-05-23 16:03:47

标签: mysql sql indexing

以下是我的表格。有些专栏没有必要复制,但这是我真实世界的实际表格,而且它很小,所以我发布了整个内容:

CREATE TABLE `Alarms` (
  `AlarmId`        INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '32-bit ID',
  `Code`           BIGINT(20) UNSIGNED NOT NULL,
  `GenerationTime` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
  `ExpiryTime`     TIMESTAMP NULL DEFAULT NULL,
  `ExpiryCause`    ENUM('natural','artificial') DEFAULT NULL,
  `AckTime`        TIMESTAMP NULL DEFAULT NULL,
  `ClearTime`      TIMESTAMP NULL DEFAULT NULL,

  PRIMARY KEY (`AlarmId`),
  KEY `AlarmExpiry` (`ExpiryTime`),
  KEY `AlarmLevels` (`AckTime`,`ClearTime`),
  KEY `AlarmTime`   (`GenerationTime`),
) ENGINE=INNODB;

现在,最低限度,如何重现我的问题。

以下使用AlarmTime索引进行快速排序和LIMIT

SELECT
   `AlarmId`
FROM
   `Alarms`
ORDER BY
   `GenerationTime` DESC
 , `AlarmId` DESC
LIMIT
   2055820, 20

说明:

id    select_type    table   type   possible_keys   key         key_len   ref      rows      Extra
1     SIMPLE         Alarms  index  (NULL)          AlarmTime   4         (NULL)   2050259   Using index

以下内容并非如此。它缓慢地进行文件分析(几秒钟,行长约2米):

SELECT
   `AlarmId`
 , `Code`
FROM
   `Alarms`
ORDER BY
   `GenerationTime` DESC
 , `AlarmId` DESC
LIMIT
   2055820, 20

说明:

id    select_type    table   type   possible_keys   key         key_len   ref      rows      Extra
1     SIMPLE         Alarms  ALL    (NULL)          (NULL)      (NULL)    (NULL)   2050259   Using filesort

我不明白为什么LIMIT似乎阻止我能够选择任何不会成为桌上钥匙一部分的列。

这两个看似相关的手册页[1] [2]似乎没有提到这种情况。

我错过了哪些表索引的细微差别?

[1]:https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html
[2]:https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

2 个答案:

答案 0 :(得分:2)

在对我的MySQL服务器进行一些测试后,我只能确认Sloan Thrasher在说什么......

优化器实际上考虑了所选列以及Order By列。添加以下索引是否需要。

ALTER TABLE alarms 
ADD INDEX GenerationTime_AlarmId_Code (GenerationTime DESC, AlarmId DESC, Code);

<强>更新

我已经确认了您的数据集...以DESC顺序创建上述组合索引,产生最佳结果。从我们系统的2.278秒到.765秒。

作为旁注,如果您的订单依据中不需要AlarmId DESC

(例如,如果您有2个具有完全相同GenerationTime的AlarmId,则不必关心AlarmId首先在列表中显示)

我没有理由使用它。所以我说从Order by中删除它。制作稍快一些。 &#34; 0.749&#34;

另一方面,如果你需要按照相同的顺序将你的AlarmId列为总是列出,你还需要按确定性列(PK)分组,以确保它们以相同的顺序显示。见Here

如果确保使用和不使用LIMIT的相同行顺序很重要,请在ORDER BY子句中包含其他列以使订单具有确定性。

答案 1 :(得分:1)

首先,LIMIT 2055820, 20 糟糕不切实际。有人坐在那里按UI上的[Next] 102,791次?!我对此表示怀疑。你真的有一个延伸到那么远的用户界面吗?您是否了解查询必须通过2,055,820条记录才能获得您想要的20条记录?

抱歉要严厉,但我讨厌错误的UI设计。

如前所述,INDEX(GenerationTime, AlarmId, Code)是唯一可以添加以帮助查询的内容。它有助于SELECT的两种变体。 (注意:DESC在这种情况下不需要 INDEX中允许使用该语法,但在版本8.0之前将被忽略。)

如果确实确实需要分页,请参阅my article了解如何&#34;记住你离开的地方&#34;。

如果您的UI正在探测大型数据集,那么请考虑提供一种方法来选择(或输入)GenerationTime的值作为此大流中的起点。

一种想法是将GenerationTime分解为大约50个相等的范围;让用户选择;然后在那里为他提供了50个细分。 然后扫描只有大约50页,每行20行 - 很多更易于管理。