一提到索引,可能就会想到B+树索引、Hash索引、聚簇索引、主键索引、唯一索引、联合索引等等,但这些名词并不能混为一谈,他们有重复的部分,是从不同方面给索引取的名字。
从数据结构上来讲:B+树索引、Hash索引、Full-text 索引;
从物理存储上来讲:聚簇索引(主键索引)、二级索引(辅助索引);
从索引字段上来讲:主键索引、唯一索引、普通索引、前缀索引;
从字段数量上来讲:单列索引、联合索引;
那么我就从各个方面来介绍一下这些索引:
1、从数据结构上来讲
1.1 B+树索引
InnoDB 是在 MySQL 5.5 之后成为默认的 MySQL 存储引擎,B+Tree 索引类型也是 MySQL 存储引擎采用最多的索引类型。
B+Tree 是一种多叉树,叶子节点才存放数据,非叶子节点只存放索引,而且每个节点里的数据是按主键顺序存放的。每一层父节点的索引值都会出现在下层子节点的索引值中,因此在叶子节点中,包括了所有的索引值信息,并且每一个叶子节点都有两个指针,分别指向下一个叶子节点和上一个叶子节点,形成一个双向链表。
- 高效的查找、插入和删除操作。
- 优化的磁盘读写:B+树的结构减少了磁盘I/O操作,因为它将节点大小设置为与磁盘块相等,这减少了磁盘访问次数。
- 支持范围查询:叶子节点的链表结构使得B+树特别适合执行范围查询,能够快速地遍历指定范围的所有值。
1.2 Hash索引
Memory引擎默认支持哈希索引,如果多个Hash值相同,出现哈希碰撞,那么索引就以链表方式存储。InnoDB或MyISAM存储引擎页支持Hash索引,但是需要通过伪Hash索引来实现,叫自适应Hash索引。
优点:
- 查询速度非常快,只需一次哈希计算即可定位到相应的键值。
- 适用于等值查询。
缺点:
- 无法支持范围查询,因为哈希索引不具备排序特性。
- 无法进行模糊查询(例如
like 'xxx%'
)。 - 内存占用较高,因为需要存储哈希值。
1.3 全文索引:
Full-text索引一般使用倒排索引实现。倒排索引同B+tree索引一样,也是一种索引结构。
MySQL中InnoDB存储引擎在之前版本中是不支持全文检索的,要使用全文检索的话只能使用MySIAM存储引擎。在 MySQL 5.6.4 版本中InnoDB存储引擎才开始支持Full-text索引。
对于文本类型的大对象,或者较大的CHAR类型的数据,如果使用普通索引,那么匹配文本前几个字符还是可行的,但是想要匹配文本中间的几个单词,那么就要使用LIKE %word%来匹配,这样需要很长的时间来处理,响应时间会大大增加,这种情况,就可使用时FULLTEXT索引了,在生成FULLTEXT索引时,会为文本生成一份单词的清单,在索引时及根据这个单词的清单来索引。
- 优点:
- 用于全文搜索,支持关键词匹配。
- 适用于模糊查询和文本内容搜索。
- 缺点:
- 占用空间较大。
- 不适合精确匹配
2、从物理存储上来讲
2.1聚簇索引
主键索引的 B+Tree 的叶子节点存放的是实际数据,所有完整的用户记录都存放在主键索引的 B+Tree 的叶子节点里,当通过聚簇索引查询的时候,不需要回表,因为这个索引中存储了这张表中所有数据。
聚簇索引的创建:
- 如果有主键,默认会使用主键作为聚簇索引的索引键(key);
- 如果没有主键,就选择第一个不包含 NULL 值的唯一列作为聚簇索引的索引键(key);
- 在上面两个都没有的情况下,InnoDB 将自动生成一个隐式自增 id 列作为聚簇索引的索引键(key);
2.2 非聚簇索引
非聚集索引的结构和聚集索引基本相同(非叶子结点存储的都是索引指针),区别在于叶子节点存放的不是行数据而是数据主键。因此在使用非聚集索引进行查找时,需要先查找到主键值,然后再到聚集索引中进行查找。这过程叫做回表。适合单行查找和特定值查找操作。可以有多个非聚集索引。
回表查询简单来说就是通过非聚集索引查询数据时,得不到完整的数据内容,需要再次查询主键索引来获得数据内容。
所以如果使用非聚集索引后还需要使用其他字段的(包括在where条件中或者select子句中),则需要通过主键索引回表到聚集索引获取其他字段。如果是非聚集索引可以满足SQL语句的所有字段的,则被称为全覆盖索引,没有回表开销。
避免回表查询问题,创建联合索引,如根据姓名查性别,那就建立一个姓名和性别的联合索引,那么再这个联合索引中进行查询的话,有查询字段性别,此时就不用回表,这种行为成为索引覆盖。索引覆盖就是指索引的叶子节点已经包含了查询的数据,满足查询要求,没必要再回表进行查询。
3、从索引字段上来讲
3.1 主键索引:
- 主键索引是一种特殊的唯一索引,要求索引列的值必须唯一且不可为空。
- 每个表只能有一个主键索引,通常用于标识唯一性的列,如用户ID或订单号。
3.2 唯一索引:
- 唯一索引要求索引列的值必须唯一,但允许包含空值。
- 适用于需要唯一性约束的列,如邮箱地址或用户名。
3.3普通索引:
- 普通索引是最基本的索引类型,没有特殊限制。
- 可以在单列上创建,也可以是组合索引。
- 适用于经常需要查询的列。
3.4前缀索引:
- 前缀索引是指对字符类型字段的前几个字符建立的索引,而不是在整个字段上建立的索引,前缀索引可以建立在字段类型为 char、 varchar、binary、varbinary 的列上。
- 使用前缀索引的目的是为了减少索引占用的存储空间,提升查询效率。
4、从字段数量上来讲
4.1 单列索引
- 单列索引是最基本的索引类型,仅对一个列进行索引。
- 适用于经常需要查询的单个列。
- 例如,在用户表中对年龄(age)字段建立单列索引。
4.2联合索引
- 联合索引由多个列组成,可以同时对多个列进行索引。
- 适用于组合搜索,效率高于单列索引。
- 注意选择联合索引的列顺序,最左前缀原则。
最左前缀原则
查询中的过滤条件必须从索引的最左边开始,并且不能跳过中间的列。只有当查询条件与索引的最左前缀完全匹配时,索引才能被充分利用。
比如,如果创建了一个 (a, b, c)
联合索引,如果查询条件是以下这几种,就可以匹配上联合索引:
- where a=1;
- where a=1 and b=2 and c=3;
- where a=1 and b=2;
需要注意的是,因为有查询优化器,所以 a 字段在 where 子句的顺序并不重要。
但是,如果查询条件是以下这几种,因为不符合最左匹配原则,所以就无法匹配上联合索引,联合索引就会失效:
- where b=2;
- where c=3;
- where b=2 and c=3;
上面这些查询条件之所以会失效,是因为(a, b, c)
联合索引,是先按 a 排序,在 a 相同的情况再按 b 排序,在 b 相同的情况再按 c 排序。所以,b 和 c 是全局无序,局部相对有序的,这样在没有遵循最左匹配原则的情况下,是无法利用到索引的。