【问题标题】:Why MySQL only use composite index but not separate indexes?为什么 MySQL 只使用复合索引而不使用单独索引?
【发布时间】:2019-01-17 07:36:52
【问题描述】:

我有下表

CREATE TABLE `test` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `a` int(11) NOT NULL,
  `b` int(11) NOT NULL
);

我需要做以下查询

SELECT * FROM `test` ORDER BY a, b LIMIT 1;

如果我添加一个复合索引

ALTER TABLE `t_test` ADD INDEX a_b(`a`, `b`);

有效

> EXPLAIN SELECT * FROM `test` ORDER BY a, b LIMIT 1;
+------+-------------+-------+-------+---------------+------+---------+------+------+-------+
| id   | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra |
+------+-------------+-------+-------+---------------+------+---------+------+------+-------+
|    1 | SIMPLE      | test  | index | NULL          | a_b  | 8       | NULL |    1 |       |
+------+-------------+-------+-------+---------------+------+---------+------+------+-------+

但是如果我分别添加两个索引

ALTER TABLE `t_test` ADD INDEX a(`a`), ADD INDEX b(`b`);

失败了

> EXPLAIN SELECT * FROM `test` ORDER BY a, b LIMIT 1;
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+
|    1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL |    2 | Using filesort |
+------+-------------+-------+------+---------------+------+---------+------+------+----------------+

即使我添加FORCE INDEX,它也不起作用。

据我个人理解,它应该同时使用index aindex b,并且比复合索引小一点。

即使我错了,它至少应该先使用index a,然后使用filesort对b进行排序。

这种排序运算符真的不能使用分隔索引吗?如果是,请解释为什么它不起作用。如果没有,您是否有任何解决方案让它与单独的索引一起使用?提前致谢。

编辑

例如,我有 100 行。我可以先使用index a 对它们进行排序。然后在每个具有相同a 值的组中,我可以使用index b 对它们进行排序。

为什么这种方式不能在 MySQL 上工作?

【问题讨论】:

    标签: mysql indexing


    【解决方案1】:

    当索引(复合或非复合)用于“排序”时,MySQL 按顺序读取数据,根本不进行排序。这对于多个索引是不可能的。

    在第一个索引上按顺序读取然后文件排序是可能的,但它不太可能更快,所以 MySQL 不这样做。

    如果你真的需要这样做,你可以使用这样的子查询:

        SELECT ...
        FROM (
                SELECT primary_key
                FROM table1
                ORDER BY field1
                LIMIT 15
        ) tmp
        JOIN table1 t ON t.primary_key = tmp.primary_key
        ORDER BY field1, field2
    

    当您有 LIMIT 并且由于某种原因无法添加复合索引时,这可能很有用。

    【讨论】:

    • 为什么没有更快?例如你有 (a,b) (3,1), (3,0), (2,1), (2,0),你首先需要排序a 然后ba 上的索引必须有助于排序。你的查询很慢,因为你做JOIN,有额外的费用。
    • 只有在 100k 行上存在例如 LIMIT 20 时,此查询才会很快。 MySQL 仍然必须执行“类似 JOIN 的操作”才能从主(聚集)索引中获取数据,这比显式 JOIN 更快,但仍然必须发生。
    • 正如我已经提到的,没有“使用索引排序”这样的东西。索引是一个有序的数据结构(通常是BTREE),自然是按顺序读取的。如果您读取两个索引,则需要组合结果,然后从聚集索引中添加其他内容。与仅读取聚集索引和排序相比,这将具有更高的 IO、内存成本和可能更高的 CPU 成本。
    • 那么你的意思是读取两个索引比一个复合索引慢吗?我完全同意你的看法。但是我认为读取两个索引比没有索引快(引擎需要在运行时排序,它当然比使用索引慢),对吧?如果是,为什么mysql不使用两个索引而不是没有索引?
    • 实际上,如果您读取所有行(如这里的情况),则从两个索引读取和读取复合(具有相同列)几乎相同。但是读取两个索引 + 合并数据 + 从主索引读取和合并可能比从主索引读取 + 排序要慢(出于同样的原因,如果您删除 LIMIT,我的查询会很慢)。
    猜你喜欢
    • 2011-05-30
    • 2012-03-23
    • 1970-01-01
    • 2013-10-29
    • 2016-06-24
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    相关资源
    最近更新 更多