关于索引:MySQL索引如何工作?

How do MySQL indexes work?

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

我知道,这是离题的,但如果有人能详细地向我解释这一点,我会非常非常感谢。


基本上,表上的索引工作起来就像书中的索引(这就是名称的来源):

假设你有一本关于数据库的书,你想找到一些关于存储的信息。如果没有索引(假设没有其他帮助,比如目录),你就必须逐个浏览页面,直到找到主题(这是一个full table scan)。另一方面,索引有一个关键字列表,因此您可以参考索引,并在113-120231和354页上提到storage。然后您可以直接翻到这些页面,而不需要搜索(这是一个带有索引的搜索,速度更快)。

当然,索引有多有用取决于许多事情——举几个例子,使用上面的比喻:

  • 如果你有一本关于数据库的书,并且为"数据库"这个词编制了索引,你会在1-59、61-290和292-400页上看到它。在这种情况下,索引没有多大帮助,而且逐个浏览页面可能会更快(在数据库中,这是"较差的选择性")。
  • 对于一本10页的书来说,做一个索引是没有意义的,因为你可能最终会得到一本10页的书,前面加上一个5页的索引,这很愚蠢——只需扫描10页就可以了。
  • 索引也需要有用-通常没有索引点,例如每页字母"L"的频率。


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

索引有不同的类型,它们是在存储层中实现的,因此它们之间没有标准,而且它们还依赖于您使用的存储引擎。

InnoDB和B+树索引

对于InnoDB,最常见的索引类型是基于B+树的索引,它按排序顺序存储元素。另外,您不必访问真实的表来获取索引值,这使得您的查询返回速度更快。

关于这个索引类型的"问题"是您必须查询最左边的值才能使用索引。因此,如果索引有两列,比如姓和名,那么查询这些字段的顺序非常重要。

因此,给出下表:

1
2
3
4
5
CREATE TABLE person (
    last_name VARCHAR(50) NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    INDEX (last_name, first_name)
);

此查询将利用索引:

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

但是下面的一个不会

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

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

最后一个例子更糟:

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

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

哈希索引

不幸的是,这是一种不同的索引类型,只有内存后端支持。它的速度非常快,但只对完全查找有用,这意味着您不能将它用于像><LIKE这样的操作。

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

如果您有一个大的VARCHAR字段,那么您可以在使用B树时"模拟"哈希索引的使用,方法是创建另一个列并在其上保存一个大值的哈希。假设您在一个字段中存储一个URL,并且值很大。您还可以创建一个名为url_hash的整数字段,并在插入URL时使用类似CRC32的哈希函数或任何其他哈希函数对其进行哈希。然后,当需要查询这个值时,可以这样做:

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

上面这个例子的问题是,由于CRC32函数生成了一个非常小的散列,所以散列值中会有很多冲突。如果需要精确的值,可以通过执行以下操作来解决此问题:

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

即使碰撞次数很高,仍然值得散列,因为您将只对重复散列执行第二次比较(字符串1)。

不幸的是,使用这种技术,您仍然需要点击表来比较url字段。

总结

每次您想谈论优化时,您可能会考虑的一些事实:

  • 整数比较比字符串比较快得多。可以用InnoDB中散列索引的仿真实例来说明。

  • 也许,在一个过程中添加额外的步骤会使它更快,而不是更慢。可以通过将SELECT分成两个步骤来优化它,第一个步骤将值存储在新创建的内存表中,然后对第二个表执行更重的查询来说明这一点。

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

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


    基本上,索引是按顺序排序的所有键的映射。按照顺序排列一个列表,它可以这样做,而不是检查每个键:

    1:在列表中间-比我要找的高还是低?

    2:如果高,转到中间和底部之间的中间点,如果低,中间和顶部

    3:是高还是低?再跳到中间点,等等。

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

    很明显,有很多复杂的事情,但这给了你基本的想法。


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

    他们的工作方式太宽泛了,一篇这样的文章无法涵盖。

    下面是我所看到的索引的最佳解释之一。不幸的是,它是针对SQL Server而不是MySQL的。我不知道这两者有多相似…


    在此视频中获取有关索引的详细信息

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

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

    可以使用一列或多列创建索引。例如,我们可以使用tutorial-author在tutorials_tbl上创建索引。

    1
    2
    CREATE UNIQUE INDEX AUTHOR_INDEX
    ON tutorials_tbl (tutorial_author)

    可以在表上创建简单索引。只需从查询中省略unique关键字即可创建简单索引。简单索引允许表中存在重复值。

    如果要按降序对列中的值进行索引,可以在列名称后面添加保留字desc。

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