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

Jam*_*vec 35 mysql sql indexing performance

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

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

例如,来自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)
);
Run Code Online (Sandbox Code Playgroud)

在任何情况下,如果以下情况会更好,或者它是否相同,会不会有任何好处?

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)
);
Run Code Online (Sandbox Code Playgroud)

Specificially:

INDEX name (last_name,first_name)
Run Code Online (Sandbox Code Playgroud)

VS

INDEX name (first_name,last_name)
Run Code Online (Sandbox Code Playgroud)

Bil*_*win 65

在讨论多列索引时,我使用类比电话簿.电话簿基本上是姓氏的索引,然后是名字.因此排序顺序取决于哪个"列"是第一个.搜索分为几类:

  1. 如果您查找姓氏为Smith的人,您可以轻松找到它们,因为该书按姓氏排序.

  2. 如果你查找姓名为约翰的人,那么电话簿就无济于事,因为约翰斯分散在整本书中.你必须扫描整本电话簿才能找到它们.

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

如果你有一个按姓氏和姓氏排序的电话簿,那么书的分类将有助于你进入上述案例#2和#3,但不是案例#1.

这解释了查找确切值的情况,但如果您按值范围查找会怎么样?假设你想找到所有姓名为John且姓氏以'S'开头的人(Smith,Saunders,Staunton,Sherman等).约翰斯在每个姓氏中的"J"下排序,但是如果你想要所有姓氏的所有姓氏以"S"开头,那么约翰斯就不会归为一类.它们再次分散,因此您最终必须扫描姓氏以"S"开头的所有姓名.如果电话簿是按名字和姓氏组织的,那么你会发现所有的约翰斯在一起,然后在约翰内,所有'S'姓都将被组合在一起.

因此,多列索引中列的顺序绝对重要.一种类型的查询可能需要索引的特定列顺序.如果您有多种类型的查询,则可能需要多个索引来帮助它们,并且列具有不同的顺序.

您可以阅读我的演示文稿如何设计索引,真的可以获得更多信息.

  • 我非常喜欢电讯簿中的类比 (12认同)
  • @CMCDragonkai,再想想电话簿的比喻。它*是*按多列键排序:`lastname`、`firstname`。如果您使用“ORDER BY lastname, firstname”执行查询请求数据,那么查询优化器会说“嘿!它已经按该顺序存储了!我可以按其自然顺序读取它并将其发送给用户,我不不用重新整理!” (3认同)
  • @CMCDragonkai,是的,这是一个问题。顺便说一下,这么快就建立了联系,做得很好。许多开发人员都无法预料到这一点。MySQL 8.0 正在开发一项功能来解决这个问题。创建索引时,您可以声明哪些列按升序排列,哪些列按降序排列。然后,如果您使用与该索引中列的“方向”匹配的相同 ASC 和 DESC 组合进行搜索,则可以使用该索引优化查询。请参阅http://mysqlserverteam.com/mysql-8-0-labs-descending-indexes-in-mysql/ (2认同)

Gor*_*off 12

这两个指标是不同的.这在MySQL和其他数据库中都是如此.MySQL在解释文档中的不同方面做得非常好.

考虑两个索引:

create index idx_lf on name(last_name, first_name);
create index idx_fl on name(first_name, last_name);
Run Code Online (Sandbox Code Playgroud)

这两个应该同样适用于:

where last_name = XXX and first_name = YYY
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

对于其中许多情况,可能会使用这两个索引,但其中一个是最佳的.例如,考虑使用查询的idx_lf:

where first_name = XXX order by last_name
Run Code Online (Sandbox Code Playgroud)

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


Gra*_*les 5

一般规则是,你想要把最具选择性的 - 也就是那个能给你最少的结果 - 首先.因此,如果您在一个表上创建一个多列索引,其中包含status10个可能值的dateAdded列,以及一个列,您通常会编写类似的查询

SELECT * FROM myTable WHERE status='active' and dateAdded='2010-10-01'
Run Code Online (Sandbox Code Playgroud)

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

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