一般情况下,数据库做了两件事情:存储数据和检索数据。检索数据是数据库中使用最多的一个功能。本篇文主要讨论MySQL数据库中InnoDB存储引擎下的索引的原理和使用。本文主要从以下几个方面展开:

索引的概念

MySQL官方对索引的解释是这样的,原文如下:

Indexes are used to find rows with specific column values quickly. Without an index, MySQL must begin with the first row and then read through the entire table to find the relevant rows. The larger the table, the more this costs. If the table has an index for the columns in question, MySQL can quickly determine the position to seek to in the middle of the data file without having to look at all the data. This is much faster than reading every row sequentially.

翻译如下:

索引是用来快速查找特定列为具体的值的行的。没有索引,MySQL必须从头到尾扫描整张表来找打相关的行。表越大,消耗越大。如果查询列上有索引,MySQL能够快速的确定要查找数据所在数据文件的具体位置而不用扫描所有数据,这比按顺序读取每一行数据快很多。

简单的来说,索引就像书本的目录,通过目录可以快速的确定要查找内容的具体位置。通过索引,加快查询速度,降低查询消耗。

MySQL中的索引是存储引擎实现的,每种存储引擎使用的索引不同。以下只讨论InnoDB中的索引。

InnoDB索引原理

InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+树中的。每一个索引在 InnoDB 里面对应一棵 B+ 树。数据存储在叶子节点上。详细结构如下:

B+树索引结构

索引的分类

索引可以有很多类型,如聚簇索引、非聚簇索引、唯一索引、复合索引、全文索引、空间索引等,在此只讨论聚簇索引和非聚簇索引。

聚簇索引

在InnoDB表中,数据是按照主键索引的顺序来组织的,这种表称为索引组织表,而主键索引称为聚簇索引。创建表后,如果表上有主键,则聚簇索引就是主键索引,否则使用非空唯一索引来创建聚簇索引,没有主键也没有非空唯一索引,InnoDB会创建一个隐藏的自增ID来作为聚簇索引的字段。一张表只能有一个聚簇索引。

聚簇索引的非叶子节点存放每一个键的值,叶子节点存放每一行的数据。

辅助索引

除了聚簇索引之外的索引都可以称为辅助索引,辅助索引和聚簇索引的区别在于,辅助索引的叶子节点存储的是主键的键值,一张表可以有多个辅助索引。使用辅助索引来查找数据时,一般需要经过两个过程:一是通过辅助索引查找到对应的主键值,二是通过主键值去查找到需要的数据,这个过程称为回表。当创建的索引上包含多个字段,覆盖了要查找的字段时,查询不需要回表就可以查询到需要的数据,这种索引称为覆盖索引。

索引的优缺点

优点

  • 提高数据检索的效率,降低数据库的IO成本

    在数据库查询的过程中,本质上是尽量减少查找的数据量,降低磁盘IO来提高查询的效率。

  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

    当对数据进行排序时,如果排序字段没有索引,将消耗大量的CPU来进行排序操作。数据库较大的情况下甚至会使用磁盘来进行排序,查询效率低下。

缺点

  • 索引会占用磁盘空间

    索引也是一张表,表中保存了主键和索引字段,

  • 降低更新效率

    在对表做更新操作的时候,MySQL不仅要保存数据,还需要保存索引,每次更新有索引的字段,都会因为更新的键值改变索引的信息。

如何利用索引来优化查询

既然索引可以提高查询效率,那是否在所有列上都建立索引就可以了?答案是不一定。并不是所有的索引都能够提高查询效率。那如何建立合适的索引来提高查询效率?

索引的建立需要符合以下原则:

  • 查询频次较高,且数据量较大的表
  • 尽量使用唯一索引
  • 选择区分度较高的列建立索引
  • 对于建立索引的表,更新不应频繁
  • 选择索引列时应选择where条件或者on子句中使用的字段
  • 可以使用覆盖索引,避免回表操作
  • 表记录较少(如配置表)或者字段区分度较低(如性别)时不应建立索引
  • 字段值较长时不建议建立索引(可以考虑建立前缀索引)
  • 查询中的排序字段应该创建索引