MySQL中多列索引的字段顺序是不是重要

Posted

技术标签:

【中文标题】MySQL中多列索引的字段顺序是不是重要【英文标题】:Does Order of Fields of Multi-Column Index in MySQL MatterMySQL中多列索引的字段顺序是否重要 【发布时间】:2014-08-10 11:54:38 【问题描述】:

我知道索引的重要性以及连接顺序如何改变性能。我已经阅读了大量与多列索引相关的内容,但没有找到我的问题的答案。

我很好奇我是否做一个多列索引,如果指定它们的顺序是否重要。我的猜测是它不会,并且引擎会将它们视为一个组,其中排序无关紧要。但我想验证一下。

例如来自mysql的网站(http://dev.mysql.com/doc/refman/5.0/en/multiple-column-indexes.html)

CREATE TABLE test (
    id         INT NOT NULL,
    last_name  CHAR(30) NOT NULL,
    first_name CHAR(30) NOT NULL,
    PRIMARY KEY (id),
    INDEX name (last_name,first_name)
);

在以下情况下会有什么好处,或者它是等效的吗?

CREATE TABLE test (
    id         INT NOT NULL,
    last_name  CHAR(30) NOT NULL,
    first_name CHAR(30) NOT NULL,
    PRIMARY KEY (id),
    INDEX name (first_name,last_name)
);

具体来说:

INDEX name (last_name,first_name)

INDEX name (first_name,last_name)

【问题讨论】:

【参考方案1】:

一般规则是,在多列索引中,您希望将最有选择性的——即给你的结果最少的那个——放在第一位。因此,如果您要在一个表上创建一个多列索引,该表的 status 列有 10 个可能的值,还有一个 dateAdded 列,并且您通常会编写类似的查询

SELECT * FROM myTable WHERE status='active' and dateAdded='2010-10-01'

...那么您首先需要dateAdded,因为这会将扫描限制在几行而不是您的行的 10%(或任何“活动”比例)。

这需要相当多的思考和调整;你应该看看 Lahdenmaki 和 Leach 的书。

【讨论】:

我同意选择时的顺序,但我的问题与多列索引中列的顺序有关。 不,我也在谈论创建索引。但是您创建索引来支持您的数据库将负责的查询类型——因此是示例。 在您的具体示例中,索引中的列顺序可能在大多数地方都无关紧要,因为名字和姓氏分布大致相等。但是,如果您在越南,那里几乎没有不同的姓氏,那么您会希望 First Name 在索引中排在首位。 where子句中列条件的顺序无关紧要,你的情况完全错误 我会重复或重复。我没有讨论 WHERE 子句中的列顺序。 (解析器不关心。)多列索引的列顺序确实很重要。【参考方案2】:

在讨论多列索引时,我将其比喻为电话簿。电话簿基本上是姓氏索引,然后是名字。所以排序顺序取决于哪个“列”在前。搜索分为几类:

    如果您查找姓氏为 Smith 的人,您可以轻松找到他们,因为这本书是按姓氏排序的。

    如果您查找名字是 John 的人,电话簿无济于事,因为 John 分散在整本书中。你必须扫描整个电话簿才能找到它们。

    如果您查找具有特定姓氏 Smith 和特定名字 John 的人,这本书会有所帮助,因为您会发现 Smiths 排序在一起,并且在该 Smiths 组中,Johns 也按排序顺序找到.

如果您的电话簿按名字排序,然后按姓氏排序,则该电话簿的排序将在上述情况#2 和#3 中对您有所帮助,但对情况#1 没有帮助。

这就解释了查找精确值的情况,但是如果您是按值的范围查找呢?假设您要查找名字为 John 且姓氏以“S”开头的所有人(Smith、Saunders、Staunton、Sherman 等)。 Johns 在每个姓氏中按“J”排序,但如果您想要所有姓氏以“S”开头的所有 Johns,则不会将 Johns 分组在一起。它们再次分散,因此您最终不得不扫描所有姓氏以“S”开头的名字。然而,如果电话簿是按名字然后按姓氏组织的,那么您会发现所有 Johns 在一起,然后在 Johns 中,所有“S”姓氏将被组合在一起。

因此,多列索引中的列顺序肯定很重要。一种类型的查询可能需要索引的特定列顺序。如果您有多种类型的查询,您可能需要多个索引来帮助它们,列的顺序不同。

您可以阅读我的演示文稿How to Design Indexes, Really 了解更多信息。

【讨论】:

我非常喜欢电话簿的类比 多列索引是否有助于多列​​排序?还是它只对多列约束有帮助? @CMCDragonkai,再次想到电话簿的类比。它按多列键排序:lastnamefirstname。如果您使用ORDER BY lastname, firstname 查询数据,那么查询优化器会说“嘿!它已经按该顺序存储了!我可以按照自然顺序读取它并将其发送给用户,我不必重新排序!” 但是如果顺序是 ASC 和 DESC 或 DESC 和 ASC 就不行了,对吧?它只适用于 ASC 和 ASC 或 DESC 和 DESC。 @CMCDragonkai,是的,这是个问题。顺便说一句,如此迅速地建立这种联系做得很好。许多开发人员无法预料到这一点。 MySQL 8.0 正在开发一个特性来处理这个问题。创建索引时,可以声明哪些列升序,哪些列降序。稍后,如果您使用与该索引中列的“方向”匹配的 ASC 和 DESC 的相同组合进行搜索,则可以使用该索引优化查询。见mysqlserverteam.com/mysql-8-0-labs-descending-indexes-in-mysql【参考方案3】:

这两个索引不同。在 MySQL 和其他数据库中都是如此。 MySQL 很好地解释了documentation 中的不同之处。

考虑两个索引:

create index idx_lf on name(last_name, first_name);
create index idx_fl on name(first_name, last_name);

这两个应该同样适用于:

where last_name = XXX and first_name = YYY

idx_lf 将最适合以下条件:

where last_name = XXX
where last_name like 'X%'
where last_name = XXX and first_name like 'Y%'
where last_name = XXX order by first_name

idx_fl 将最适合以下情况:

where first_name = YYY
where first_name like 'Y%'
where first_name = YYY and last_name like 'X%'
where first_name = XXX order by last_name

对于其中许多情况,两个索引可能都可以使用,但一个是最佳的。例如,考虑带有查询的 idx_lf:

where first_name = XXX order by last_name

MySQL 可以使用 idx_lf 读取整个表,然后在order by 之后进行过滤。我不认为这在实践中是一个优化选项(对于 MySQL),但这可能发生在其他数据库中。

【讨论】:

+1 用于指向 MySQL 文档的链接,您的示例进一步强调了顺序在您进行的查询的上下文中很重要。 感谢order by提及

以上是关于MySQL中多列索引的字段顺序是不是重要的主要内容,如果未能解决你的问题,请参考以下文章

Mysql-索引类型篇

MySQL学习总结索引

MySQL索引失效原因

mysql联合索引字段顺序

多列 BRIN 列顺序是不是重要?

WHERE 子句中的字段顺序会影响 MySQL 的性能吗?