【问题标题】:Understanding Indexes in MySQL了解 MySQL 中的索引
【发布时间】:2013-10-30 12:12:08
【问题描述】:

我正在尝试了解 MySQL 中的索引。我知道在表中创建的索引可以加快查询的执行速度,也可以减慢行的插入和更新速度。

创建索引时,我在名为 authors 的表上使用了此查询,该表包含 (AuthorNum, AuthorFName, AuthorLName, ...)

Create index Index_1 on Authors ([What to put here]);

我知道我必须输入一个列名,但是哪个?

当用户查询表格时,我是否必须将要比较的列名放在 Where 语句中?

【问题讨论】:

  • 通常(但不总是),在大多数情况下,索引由where 确定。用户如何搜索Authors?按名字,姓氏,...?
  • 我假设他们会搜索 AuthorFName。
  • 索引不是专门从一列构建的,有些可能是从多列构建的,而另一些可能是从一列的一些信息构建的。例如,如果您有一个完整的 datetime 列,但您知道您只会按 date 过滤记录,您可以基于 datetime 列构建索引,但仅包含 date 信息。

标签: mysql sql indexing


【解决方案1】:

索引剖析

索引是数据库中不同的数据结构,是数据冗余。其主要目的是通过独立于物理排序的逻辑排序来提供索引数据的有序表示。我们使用双向链表和称为平衡搜索树(B-tree)的树结构来做到这一点。 B 树很好,因为它们保持数据排序并允许在对数时间内进行搜索、访问、插入和删除。由于双向链表,我们可以根据需要在索引上轻松地向后或向前进行各种查询。插入变得简单,因为我们只需要重新排列指向不同数据片段的指针。数据库使用这些双向链表来连接叶节点(通常在B+树或B树中),每个叶节点存储在一个页面中,并在叶节点之间建立逻辑顺序。像UPDATEINSERT 这样的操作会变慢,因为它们实际上是文件系统中的两个写入操作(一个用于表数据,一个用于索引数据)。

使用 WHERE 定义最佳索引

要定义最佳索引,您不仅必须了解索引的工作原理,还必须了解应用程序如何查询数据。例如,您必须知道出现在 WHERE 子句中的列组合。

LAST_NAMEFIRST_NAME 列的查询的一个常见限制是区分大小写。例如,我们宁愿匹配HoTingEr 等所有结果,而不是像Hotinger 那样进行精确搜索。这在WHERE 子句中很容易做到:我们只说WHERE UPPER(LAST_NAME) = UPPER('Hotinger')

但是,如果我们定义LAST_NAME 的索引并进行查询,它实际上会运行全表扫描,因为查询不在LAST_NAME 上,而是在UPPER(LAST_NAME) 上。从数据库的角度来看,这是完全不同的。因此,在这种情况下,您应该改为在 UPPER(LAST_NAME) 上定义索引。

索引不一定要针对一列。例如,如果主键是一个复合键(由多个列组成),它将创建一个连接索引,也称为组合索引。请注意,级联索引的顺序对其可用性和可伸缩性有显着影响,因此必须谨慎选择。基本上,排序应该与WHERE 子句中的排序方式相匹配。

使用 LIKE 定义最佳索引

通配符的位置有很大的不同。 LIKE 子句在遍历树时只使用before 通配符;其余的不会缩小扫描的索引范围。 LIKE 子句的前缀越有选择性,扫描的索引就越窄。这使得索引查找更快。作为提示,避免LIKE 子句以"%OTINGER%" 等通配符开头。对于全文搜索,MySQL 提供MATCHAGAINST 关键字。从 MySQL 5.6 开始,您可以拥有全文索引。查看来自 MySQL 的Full-Text Search Functions,以更深入地讨论对这些结果进行索引。

【讨论】:

    【解决方案2】:

    是的,通常您需要在查询的WHERE 子句中比较的一列或多列上建立索引以加快查询速度。

    如果您按AuthorFName 搜索,那么您会在该列上创建一个索引。如果他们按AuthorLName 搜索,那么您在该列上创建一个索引。

    不过,在这种情况下,您应该查看的是FULLTEXT index。这将允许用户输入模糊查询,这将返回一些按相关性排序的结果。

    来自MySQL Manual

    索引用于快速查找具有特定列值的行。 没有索引,MySQL 必须从第一行开始,然后读取 通过整个表找到相关的行。越大的 表,这个成本越多。如果表有列的索引 有问题,MySQL 可以快速确定要寻求的位置 数据文件的中间,而不必查看所有数据。如果 一个表有 1000 行,这至少比读取快 100 倍 依次。如果您需要访问大部分行,则更快 按顺序读取,因为这样可以最大限度地减少磁盘寻道。

    【讨论】:

      【解决方案3】:

      索引通常意味着 B-Tree。了解 B-Tree 的结构,你就会明白索引能做什么,不能做什么。

      在您的特定情况下:

      • WHERE AuthorLName = 'something'WHERE AuthorLName LIKE 'something%' 可以通过 {AuthorLName} 上的索引来加速。
      • WHERE AuthorLName = 'something AND AuthorFName = 'something else' 可以通过 {AuthorLName, AuthorFName} 或 {AuthorFName, AuthorLName} 上的复合索引来加速。
      • WHERE AuthorLName = 'something OR AuthorFName = 'something else'(没有多大意义,但在这里作为示例)可以通过有两个索引来加速:在 {AuthorLName} 在 {AuthorFName} 上。
      • WHERE AuthorLName LIKE '%something' 无法通过 B-Tree 索引(cnsider 全文索引)加速。
      • 等等……

      请参阅Use The Index, Luke!,了解比简单的 SO 帖子更全面的主题处理。

      【讨论】:

        【解决方案4】:

        限长索引:

        当使用text 列或非常大的varchar 列时,您将无法在text/varchar 的整个长度上创建索引,存在一些限制(大约 1024 个 ASCII 字符在长度)。

        在这种情况下,您可以在索引声明中指定长度。

        CREATE INDEX `my_limited_length_index` ON `my_table`(`long_text_content`(512));
        -- please notice the use of the numeric length of the index after the column name
        

        已处理值索引(显然在 PostgreSQL 而不是 MySQL 中可用):

        索引并非完全由一列构建,有些可能由多列构建,而其他索引可能仅由一列的部分信息构建。例如,如果您有一个完整的 datetime 列,但您知道您只会按 date 过滤记录,您可以基于 datetime 列构建索引,但仅包含 date 信息。

        -- `my_table` has a `created` column of type timestamp
        CREATE INDEX `my_date_created` ON `my_table`(DATE(`created`));
        -- please notice the use of the DATE function which extracts only
        -- the date from the `created` timestamp
        

        【讨论】:

          【解决方案5】:

          索引应跨越您将在 WHERE 语句中使用的列。

          为了更好的理解,这里举个例子:

          SELECT * FROM Authors WHERE AuthorNum > 10 AND AuthorLName LIKE 'A%';
          SELECT * FROM Authors WHERE AuthorLName LIKE 'Be%';
          

          如果您经常使用上面显示的查询,强烈建议您有两个索引:

          Create index AuthNum_AuthLName_Index on Authors (AuthorNum, AuthorLName);
          Create index AuthLName_Index on Authors (AuthorLName);
          

          要记住的关键点:索引应与WHERE 语句中使用的列组合相同

          【讨论】:

          • 好的,但是 MySQL 如何使用索引找到行,是否保留行号,还是什么?
          • 这个答案部分不正确,如果我没看错的话。 where 子句中表达式的顺序与索引中列的适当顺序无关。 where 子句中的表达式可以由服务器以任何逻辑上有效的顺序求值。
          • 在我的理解中,索引列将是存储在服务器 RAM 中的哈希表。在查询时,它使用 WHERE 中的哈希值搜索存储的 RAM 哈希表,并获取指向行条目的实际指针。
          • @Michael-sqlbot 谢谢你指出这一点,我已经修改了答案。从回答问题中学习新事物很棒。
          • @alandarev 哈希表为所有记录生成和存储哈希。在比较相等性时,where 子句中提供的值以相同的方式进行散列(使用相同的散列函数),然后与表中记录的散列进行比较。这意味着哈希表索引不能用于比较或相似,只能用于严格相等。这意味着在需要比较时将使用 b-tree 索引。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-09-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-09
          • 1970-01-01
          • 2013-11-23
          相关资源
          最近更新 更多