mysql - 为什么不使用键的最左边子集来优化此 ORDER BY?

标签 mysql query-optimization innodb

服务器版本:

[root@cat best]# /usr/libexec/mysqld --version
/usr/libexec/mysqld  Ver 5.1.47 for redhat-linux-gnu on i386 (Source distribution)

架构:

CREATE TABLE `Log` (
    `EntryId` INT UNSIGNED NOT NULL AUTO_INCREMENT,
    `EntryTime` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
    `Severity` ENUM(
        'LOG_LEVEL_CRITICAL',
        'LOG_LEVEL_ERROR',
        'LOG_LEVEL_WARNING',
        'LOG_LEVEL_NOTICE',
        'LOG_LEVEL_INFO',
        'LOG_LEVEL_DEBUG'
    ) NOT NULL,

    `User` TEXT,
    `Text` TEXT NOT NULL,

    PRIMARY KEY(`EntryId`),
    KEY `TimeId` (`EntryTime`,`EntryId`)
) ENGINE=InnoDB COMMENT="Log of server activity";

查询:

SELECT 
   `EntryId`,
   `EntryTime`, -- or, ideally: UNIX_TIMESTAMP(`EntryTime`) AS `EntryTime_UnixTS`
   `Severity`,
   `User`,
   `Text`
FROM `Log` 
ORDER BY `EntryTime` DESC, `EntryId` DESC
LIMIT 0, 20

根据执行计划(和观察),索引未被使用:

id  select_type  table  type  possible_keys  key  key_len  ref  rows  Extra
1   SIMPLE       Log    ALL   \N             \N   \N       \N   720   Using filesort

我已尝试通过几种方式重新组织它,但收效甚微,但最重要的是,我想了解为什么这种简单的方法会失败。我的理解是,任何键的最左侧子集都可用于优化 ORDER BY 操作。

我的索引错了吗?我可以优化查询吗?

请注意,我也想有条件地添加,例如

WHERE `Severity` <= 'LOG_LEVEL_WARNING'

尽管如果这使得解决方案非常不同,我希望首先让基本版本工作。

Reproduced on SQLFiddle under MySQL 5.5.32.

最佳答案

原因是你的索引中包含了主键。由于它是 InnoDB,默认情况下 PK 作为最左边的字段包含在所有其他索引中。即本例中的索引是 (EntryId, EntryTime, EntryId)。

解决方案是仅在 (EntryTime) 上使用此索引:

alter table Log drop index TimeId;
alter table Log add index TimeId(EntryTime);
explain SELECT     `EntryId`,    `EntryTime`,     `Severity`,    `User`,    `Text` FROM `Log`  ORDER BY `EntryTime` DESC, `EntryId` DESC LIMIT 0, 20;
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows | Extra |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------+
|  1 | SIMPLE      | Log   | index | NULL          | TimeId | 4       | NULL |   20 | NULL  |
+----+-------------+-------+-------+---------------+--------+---------+------+------+-------+

HTH

关于mysql - 为什么不使用键的最左边子集来优化此 ORDER BY?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26875660/

相关文章:

php - lastinsertId() 与 exec() 一起使用时不能与execute() 一起使用?

mysql - 选择在 where 子句中找不到的 ID

php - 为什么我的 MySQL 查询这么慢?

php - 给定一组格式为 {FIXED PREFIX}/{NUMERIC ID}/... 的 URL,如何检索具有最高 {NUMERIC ID} 的 URL?

mysql - 为什么mysql解释说 'using index',而使用的索引不包含必填字段

mysql - JDBC MySQL 尝试使用选择子查询插入多行

MySQL 插入 ISO8601 日期时间格式

mysql - 使用值列表中的子查询优化 mysql 查询

sql - (self) 按时间间隔加入

php - 为什么 TRANSACTION/COMMIT 使用 PHP/MySQL (InnoDB) 提高了性能?