什么是索引?
索引就好比书籍的目录,能够快速指引我们找到特定内容的位置。在数据库中建立索引的目的是为了迅速定位特定值和字段,因为如果没有索引,数据库就得从头到尾逐行扫描整个表,直到找到符合条件的数据。随着表中数据量的增加,这种扫描方式会变得越来越慢
,效率会受到明显的影响,索引其实就相当于字典中的目录。
索引有几种?
一:按「数据结构」分类:
-
B+tree索引: 使用B+树结构进行组织,适用于范围查询和排序操作,常见于许多关系型数据库系统。典型使用场景包括:
- 范围查询: 当需要检索一定范围内的数据时,B+tree索引可以提供高效的区间查询。
- 排序操作: 由于B+tree的有序性,支持快速的排序操作。
-
Hash索引: 使用哈希函数进行索引,适用于等值查询,但不支持范围查询和排序,常见于一些内存数据库和某些 NoSQL 数据库。典型使用场景包括:
- 等值查询: 当需要通过索引快速定位到唯一的记录时,Hash索引效果显著。
- 内存数据库: 由于Hash索引的查询效率高,适用于一些完全加载在内存中的数据库。
-
Full-text索引: 用于全文搜索,支持对文本内容进行搜索,通常在文本搜索引擎或支持全文搜索的数据库中使用。典型使用场景包括:
- 全文搜索: 当需要在大量文本数据中进行关键词搜索时,Full-text索引可以提供高效的全文检索功能。
在设计数据库时,选择适当的索引类型要考虑查询的需求。如果经常进行范围查询或排序,B+tree索引是较好的选择;如果主要进行等值查询,Hash索引可能更合适;而全文搜索场景则需要使用Full-text索引来提高检索效率。
二:按「物理存储」分类:
聚簇索引的叶子节点存储的是数据,而非聚簇索引存储的是数据的地址
- 聚簇索引(主键索引): 数据行的物理存储顺序与索引的顺序一致,通常是按照主键排序。典型使用场景包括:
- 频繁的范围查询和排序操作: 由于数据行的物理存储与索引一致,聚簇索引适合需要频繁进行范围查询和排序的情况。
- 主键查询: 主键索引通常是聚簇索引,因此主键查询效率高。
聚簇索引的每个叶子节点存储了一行完整的表数据,叶子节点间按id列递增连接,可以方便地进行顺序检索。
InnoDB表要求必须有聚簇索引,默认在主键字段上建立聚簇索引
,在没有主键字段的情况下,表的第一个非空的唯一索引将被建立为聚簇索引,在前两者都没有的情况下,InnoDB将自动生成一个隐式的自增id列,并在此列上建立聚簇索引。以MyISAM为存储引擎的表不存在聚簇索引。MyISAM表中的主键索引和非主键索引的结构是一样的,索引的叶子节点不存储表数据,存放的是表数据的地址。所以,MyISAM表可以没有主键。
MyISAM表的数据和索引是分开存储的。MyISAM表的主键索引和非主键索引的区别仅在于主键索引的B+tree上的key必须符合主键的限制,非主键索引B+tree上的key只要符合相应字段的特性就可以了
InnoDB 的B+ 树叶子节点均为数据页,存放的是用户记录。因此,所有的用户记录都存放在叶子节点上
所有叶子节点组成一个双向链表,节点之间按照 row_id 升序排列
所有的非叶子节点都是索引页,存放的记录为索引记录,同样一层的节点按照 row_id 升序排列为双向链表
这整个 B+ 树就叫做索引,此外,由于它是根据主键排序的,并且存放了所有的用户记录,这个索引又被称为聚簇索引(或者叫主键索引)。
InnoDB 的每一张表都会有这样一个聚簇索引存在,它是自动创建的。有了聚簇索引,我们能很方便的根据主键来查找数据。
- 二级索引(辅助索引): 不决定数据行的物理存储顺序,而是指向数据行的指针,通常用于支持非主键列的检索。典型使用场景包括:
- 非主键列的检索: 当需要在非主键列上进行查询时,使用辅助索引来提高检索效率。
- 避免主键变更的开销: 由于主键索引的聚簇结构,主键变更可能涉及数据行的移动,而辅助索引不受此影响。
除了默认的聚簇索引之外,InnoDB 允许自行对某些列建立索引,即为非聚簇索引,或者叫做二级索引。
由于聚簇索引是按照主键 row_id 作为排序基准的,但某些场景下,我们需要对非主键列进行查询,此时就需要二级索引了
。
在选择索引类型时,考虑到物理存储的特性对查询和维护操作的影响是很重要的。聚簇索引适用于需要频繁进行范围查询和排序的场景,而辅助索引则更适用于非主键列的检索和避免主键变更的开销。
三: 按「字段特性」分类
索引包括主键索引、唯一索引、普通索引和前缀索引。下面是对这些索引的简要解释:
-
主键索引(Primary Key Index):
- 特点: 用于唯一标识每一行数据的索引。
- 适用场景: 主键索引通常用于提高数据检索的速度,确保每行数据都有唯一标识。
-
唯一索引(Unique Index):
- 特点: 要求索引列的值是唯一的,允许空值。
- 适用场景: 适用于需要保证数据唯一性的列,可以防止重复数据的插入。
-
普通索引(Non-Unique Index):
- 特点: 普通索引没有唯一性要求,可以包含重复的值。
- 适用场景: 用于提高查询速度,适用于经常被搜索但不需要唯一性约束的列。
-
前缀索引(Prefix Index):
- 特点: 只对索引列的前缀部分进行索引,而不是整个列的值。
- 适用场景: 用于节省索引空间,适用于长文本或大字符串列,可以加速查询但牺牲了索引的精确性。
按「字段特性」分类的这些索引种类提供了在数据库设计中更灵活的选择,可以根据具体的需求和业务场景来选择适当的索引类型。
四: 按「字段个数」分类
按「字段个数」分类的索引包括单列索引和联合索引。以下是对这两种索引的简要解释:
单列索引(Single-Column Index):
特点: 只涉及一个列的索引。
适用场景: 适用于对单个列进行频繁搜索和过滤的情况,提高特定列的查询效率。
联合索引(Composite Index):
特点: 涉及多个列的组合索引,即在多个列上建立的索引。
适用场景: 适用于经常以多个列的组合作为查询条件的情况,可以加速这类联合条件的查询。
按「字段个数」分类的这两种索引类型允许数据库设计人员更灵活地根据查询需求选择适当的索引。单列索引通常用于对单一列进行快速检索,而联合索引适用于需要同时过滤多个列的查询条件。在选择索引类型时,需要综合考虑查询频率、查询条件以及对数据修改操作的影响
回表
回表是指在使用二级索引进行查询时,二级索引的叶子节点并不存放真实的数据,而是存储了记录的主键 row_id。因此,当通过二级索引找到目标后,需要再使用这个主键 row_id 去聚簇索引中查找真实的记录数据,这个过程被称为回表。
为什么二级索引不存放真实数据呢?
-
数据重复: 主键索引已经包含了所有记录,如果每个二级索引再存储一次,将会占用大量磁盘空间。
-
写入效率低下: 写入数据时需要更新每个副本,会导致写入效率降低。
因此,二级索引通常不存放真实数据。
举个回表的场景,假设查询语句为:
SELECT * FROM table WHERE x=10;
此时,根据列 x 的索引最终查找到目标记录的主键 row_id=18。然后使用 row_id=18 去聚簇索引里面查找用户记录,最终将各列的值返回。这个去聚簇索引的过程就是回表。
回表的性能消耗:
假设二级索引 B+树层级为 n,聚簇索引 B+树层级为 m。
访问二级索引本身就需要至少 n 次磁盘 I/O,然后回表也需要至少 m 次 I/O。
因此,一次查询的磁盘 I/O 为:m + n。
所以,发生回表时实际上会有一定的性能影响。在某些场景下,尽管 SELECT 语句中存在二级索引条件,MySQL 仍然选择了全表扫描,主要是因为优化器发现回表的成本比全表扫描还大。