MySQL索引如何工作?


402

我对MySQL索引的工作方式非常感兴趣,更具体地说,它们如何在不扫描整个表的情况下返回请求的数据?

我知道这是题外话,但是如果有人可以向我详细解释一下,我将非常非常感谢。



这是一个非常广泛的问题。如果您有一个不使用索引的特定查询示例,并且不知道为什么,则可以发布该索引,而人们可能会提供帮助。
Hammerite 2010年

SELECT * FROM members WHERE id = '1'-那么为什么使用索引可以更快?该索引在这里做什么?
2010年

2
这看起来像一个查询,它仅查询特定的索引记录(可能由主键标识)。索引使此操作更快,因为它存储在内存中,可以查看相应的索引行,并且其中包含指向实际数据存储位置的指针。因此,MySQL无需扫描表即可转到表中的确切位置。
Hammerite 2010年

很好,谢谢!
轻轨赛

Answers:


513

基本上,表上的索引的作用类似于书中的索引(这就是名称的来源):

假设您有一本关于数据库的书,并且想要查找有关存储的信息。没有索引(假设没有其他帮助,例如目录),则必须逐个浏览页面,直到找到主题(即full table scan)为止。另一方面,索引包含关键字列表,因此您可以查阅该索引,并storage在第113-120,231和354页中看到该索引。然后,您可以直接翻到这些页面,而无需进行搜索(即使用索引,速度更快)。

当然,索引的有用程度取决于很多事情-使用上面的比喻的几个例子:

  • 如果您有一本关于数据库的书并为“数据库”一词建立了索引,您会发现它在第1-59,61-290页和第292至400页中提到。在这种情况下,索引并没有太大帮助,它可能更快地一页一页地浏览页面(在数据库中,这是“选择性差”)。
  • 对于一本10页的书,建立索引是没有意义的,因为您可能最终得到一本10页的书,并以5页的索引为前缀,这很愚蠢-只需扫描10页并完成即可。
  • 索引也需要有用-通常没有指向索引的位置,例如每页字母“ L”的频率。

3
您在解释它是什么,而不是它在内部如何工作。
Tutu Kumari

@Tutu Kumari:请参阅问题的修订版;还可以随意修改答案以适合当前问题(请注意各种引擎和索引类型-请参见此处的文档:dev.mysql.com/doc/refman/8.0/en/index-btree-hash.html
Piskvor于

259

您必须知道的第一件事就是索引是一种避免扫描整个表以获得所需结果的方法。

索引有不同种类,它们在存储层中实现,因此它们之间没有标准,它们还取决于您使用的存储引擎。

InnoDB和B + Tree索引

对于InnoDB,最常见的索引类型是基于B + Tree的索引,该索引按排序顺序存储元素。另外,您不必访问实际表即可获取索引值,这使查询返回方式更快。

关于该索引类型的“问题”是,您必须查询最左边的值才能使用该索引。因此,如果索引有两列,例如last_name和first_name,则查询这些字段的顺序非常重要

因此,给出下表:

CREATE TABLE person (
    last_name VARCHAR(50) NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    INDEX (last_name, first_name)
);

该查询将利用索引:

SELECT last_name, first_name FROM person
WHERE last_name = "John" AND first_name LIKE "J%"

但是下面的一个不会

SELECT last_name, first_name FROM person WHERE first_name = "Constantine"

因为您要first_name首先查询该列,所以它不是索引中最左边的列。

最后一个例子更糟:

SELECT last_name, first_name FROM person WHERE first_name LIKE "%Constantine"

因为现在,您正在比较索引中最右边字段的最右边部分。

哈希索引

不幸的是,这是一种不同的索引类型,仅内存后端支持。它闪电般快速,但仅对完整查找有用,这意味着您不能将其用于><LIKE

由于它仅适用于内存后端,因此您可能不会经常使用它。我现在可以想到的主要情况是,您在内存中创建一个临时表,其中包含来自另一个选择的一组结果,并使用哈希索引在此临时表中执行许多其他选择。

如果您有一个大VARCHAR字段,则可以通过创建另一列并在其上保存一个大值的哈希值,来“模拟”使用B树时哈希索引的使用。假设您将url存储在字段中,并且值很大。您还可以创建一个名为的整数字段,url_hashCRC32在插入URL时使用像之类的哈希函数或任何其他哈希函数对URL进行哈希处理。然后,当您需要查询该值时,可以执行以下操作:

SELECT url FROM url_table WHERE url_hash=CRC32("http://gnu.org");

上面的示例的问题在于,由于该CRC32函数生成的哈希值非常小,因此最终会导致哈希值出现很多冲突。如果需要精确的值,可以通过执行以下操作解决此问题:

SELECT url FROM url_table 
WHERE url_hash=CRC32("http://gnu.org") AND url="http://gnu.org";

即使冲突数很高,仍然值得对事物进行哈希处理,因为您将仅对重复的哈希执行第二次比较(字符串“一”)。

不幸的是,使用此技术,您仍然需要点击表格以比较该url字段。

包起来

您每次谈论优化时都可能要考虑的一些事实:

  1. 整数比较比字符串比较要快得多。可以用有关在中模拟哈希索引的示例进行说明InnoDB

  2. 也许,在过程中添加其他步骤可以使其更快而不是更慢。您可以SELECT通过将a 分为两个步骤来优化它,将第一个步骤存储在新创建的内存表中,然后在第二个表上执行较重的查询,来说明这一点。

MySQL也有其他索引,但是我认为B + Tree索引是有史以来使用最多的索引,而哈希索引是个好消息,但是您可以在MySQL文档中找到其他索引。

我强烈建议您阅读“高性能MySQL”这本书,上面的答案肯定基于其有关索引的章节。


2
在上述情况下,以下查询是否有优势?1。SELECT last_name, first_name FROM person WHERE last_name= "Constantine" 2.SELECT last_name, first_name FROM person WHERE last_name LIKE "%Constantine"
Akshay Taru

1
第一查询将,第二查询将不会。使用EXPLAIN:dev.mysql.com/doc/refman/5.5/en/explain.html 要使用MySQL为第二个查询建立索引,您必须使用FULLTEXT INDEX:dev.mysql.com/doc/refman/5.5/en/fulltext- search.html
EmilioNicolás,2014年

5
我支持您,因为您的排名是127,而排名第一的答案是256。
pbarney

这对我来说是新信息,“查询这些字段的顺序非常重要。” 谢谢。
哈特里

1
@pbarney三年后它们分别接近256和512,这就是我所说的二进制明智的提高!
nanocv

43

基本上,索引是按顺序排列的所有键的映射。按顺序排列列表,然后无需检查每个键,而是可以执行以下操作:

1:转到列表的中间-高于或低于我要查找的内容?

2:如果较高,则到达中间和底部之间的中点;如果较低,则到达中间和顶部之间的中点

3:是更高还是更低?再次跳到中间点,依此类推。

使用该逻辑,您可以在大约7个步骤中在排序列表中找到一个元素,而不用检查每个项目。

显然存在复杂性,但这给了您基本的思想。


29
这称为二进制搜索。
ddlshack 2012年

谢谢,最后一个答案解释了为什么这样做更快,而不仅仅是说明数据库如何使用索引工作。
Gershon Herczeg 2013年

实际的步骤数高度依赖于数据-唯一值的数量和整个范围内的分布。7是100个值的理论最大值。有关如何计算步骤数的完整讨论,请点击此处stackoverflow.com/questions/10571170/…–
约书亚

最常见的MySQL索引是B + Tree,其工作方式类似于二进制搜索,但并不完全相同。算法复杂度相同,但搜索方式不同。参见en.wikipedia.org/wiki/B-tree
Matt

4

看一下这个链接:http : //dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html

它们的工作方式太广泛,无法在一个SO职位中讨论。

是我所见过的索引的最佳解释之一。不幸的是,它是针对SQL Server而不是MySQL。我不确定两者的相似程度...


2
不错的文章。我不知道SQL Server,但是基本工作原理非常相似。(元注:在第二篇链接文章中禁用CSS样式会取消隐藏内容)
Piskvor在2010年

3

快来看看这个视频有关索引更多详情

简单索引您可以在表上创建唯一索引。唯一索引意味着两行不能具有相同的索引值。这是在表上创建索引的语法

CREATE UNIQUE INDEX index_name
ON table_name ( column1, column2,...);

您可以使用一个或多个列来创建索引。例如,我们可以tutorials_tbl使用tutorial_author 创建索引。

CREATE UNIQUE INDEX AUTHOR_INDEX
ON tutorials_tbl (tutorial_author)

您可以在表上创建一个简单的索引。只需从查询中省略UNIQUE关键字即可创建简单索引。简单索引允许表中有重复值。

如果要按降序索引列中的值,可以在列名后添加保留字DESC。

mysql> CREATE UNIQUE INDEX AUTHOR_INDEX
ON tutorials_tbl (tutorial_author DESC)

1
欢迎使用Stack Overflow!我注意到,您所有的答案都链接到您自己的视频。请注意,禁止公开宣传自己
SL巴特-恢复莫妮卡

他想宣传自己的视频。大声笑
Ilyas karim 18-4-28

1

我要加2美分。我还远不是数据库专家,但是最近我已经阅读了一些有关该主题的文章。足以让我尝试提供ELI5。因此,这里是外行的解释。


我这样理解,索引就像表的微型镜像,几乎像是关联数组。如果给它提供一个匹配的键,则可以在一个“命令”中跳到该行。

但是,如果您没有该索引/数组,则查询解释器必须使用for循环遍历所有行并检查是否匹配(全表扫描)。

拥有索引具有额外存储空间的“缺点”(对于该微型镜而言),以换取更快查找内容的“好处”。

请注意(取决于您的数据库引擎),创建主键,外键或唯一键也会自动设置相应的索引。相同的原则基本上就是这些键为什么起作用以及如何起作用。


1

在答案列表中添加一些视觉表示。 在此处输入图片说明

MySQL使用了额外的间接层:辅助索引记录指向主索引记录,并且主索引本身保存磁盘上的行位置。如果行偏移量发生变化,则仅需要更新主索引。

注意:磁盘数据结构在图中看起来很平坦,但实际上是B +树。

资料来源:链接


1

在MySQL InnoDB中,有两种类型的索引。

  1. 主键,称为聚簇索引。索引关键字与实际记录数据一起存储在B + Tree叶子节点中。

  2. 次键是非聚集索引。这些索引仅将主键的关键字以及它们自己的索引关键字存储在B + Tree叶子节点中。因此,从二级索引搜索时,它将首先找到其主键索引关键字,然后扫描主键B + Tree来查找真实的数据记录。与主索引搜索相比,这将使二级索引变慢。但是,如果select列都在辅助索引中,则无需再次查找主索引B + Tree。这称为覆盖指数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.