认识索引
MySQL 索引是一种用于提高数据库查询性能的数据结构。例如一本书就是一个数据库,“索引”则是前面的目录,查找内容时可以先通过目录快速定位章节,而不是全书逐页比对内容。
索引归类
MySQL 索引可以按照不同维度归类总结;
数据结构
MySQL 索引分别有 HASH 和 BTREE(B+) 两种数据结构;
- HASH: 适用于等值查询,时间复杂度 O(1),但不适用范围查询和排序
- BTREE: 适用于各种查询,查询性能非常稳定
InnoDB 引擎不支持 HASH,选用的 BTREE 结构。
为什么不是二叉树,或者普通的 BTREE?
- 二叉树极端情况会退化成链表,时间复杂度下降到 O(n)
- 普通的 BTREE 每个节点都是完整的数据,导致节点数较多层级较高
- 而且 BTREE 数据分散在不同的节点,性能不稳定
存储方式
根据 BTREE 叶子节点存储记录的完整性,索引又能分成;
- 聚簇索引(clustered): 叶子节点存放的是完整的记录,包含所有列字段,以及行属性
- 非聚簇索引(secondary): 也叫辅助索引、二级索引,叶子节点存放的只有索引列和关联的聚簇索引键(主键)
索引类型
在具体添加索引时,也有以下几个类型可选;
- 主键索引(primary): 记录的唯一标识,每个表只能有一个主键
- 普通索引(normal): 最基本的索引类型
- 唯一索引(unique): 要求索引列的值是唯一的,不允许有重复
- 全文索引(fulltext): 支持全文搜索,适用于大量文本数据的列。InnoDB 自 5.6 开始支持
- 空间索引(spatial): 用于地理空间(GEO)数据类型,应用较少
数据量和并发量都不大情况下,完全可以用全文索引替代 ES,自 5.7 开始内置了中文分词插件 ngram,添加全文索引时需要手动指定,查询语句也需要使用特定语法。
-- 添加索引
ALTER TABLE user ADD FULLTEXT index_username(`username`) WITH PARSER ngram;
-- 查询数据
select * from user where match(`username`) against('建涛');
底层原理
页 InnoDB 管理存储空间的基本单位,一页大小一般是 16KB。上面所说的索引 BTREE 一个节点也就是一页,下面是页的结构示意图;
每条记录除了保存真实数据意外,还保存了指向下一条记录的指针,组成一个完整的记录链表。为避免遍历整个链表,将用户记录进行分组(最多 8 条),将每个分组最大记录的地址偏移量写入页目录中。
页内查询的过程可总结成两个步骤;
- 通过页目录二分法确定目标可能所在分组
- 遍历该分组内的所有记录匹配则返回
表中存放的记录通常需要用到很多页,页间通过双向链表相关联。与页目录类似,可以将多个页进行分组,将每个页中最小的记录写入一个新页中。由于页大小限制,新页也会有多个,从而继续分组。这也就是 BTREE 的由来。
数据页使用双向链表相连,页内记录用单向链表相连。 我的理解前者是便于索引的逆向遍历,后者总是要遍历记录链表可以节省一个指针。
高级术语
联合索引
"联合索引"是指在数据库表中的多个列上创建的一种索引。它也被称为复合索引或组合索引。
覆盖索引
"覆盖索引"是指一个查询可以通过索引本身满足查询的需求,而无需再去访问实际的数据行例如主键索引树。
反之一个查询需要通过索引查询到符合条件的主键后,再通过主键索引去获取完整的数据,这个过程称为“回表”。
-- 覆盖索引
select id, username from user where username = '涛';
-- 回表(age 列不在 username 索引树)
select id, username, age from user where username = '涛';
前缀索引
"前缀索引"是一种索引类型,它并不是对整个列值进行索引,而是只对列值的前缀(前面的一部分字符)进行索引。通过只索引列值的一部分,前缀索引可以在一定程度上减小索引的大小,从而节省存储空间和提高检索性能。
-- 前缀索引
alter table user add index index_username(username(10));
索引下推
索引下推(Index Condition Pushdown,简称ICP)是一种数据库查询优化技术,用于将部分过滤条件推送到存储引擎层进行处理,减少回表次数,从而提高查询性能。
假设联合索引为 (group_id, nickname),查询语句如下;
select * from user where group_id = 100 and nickname like '%涛%';
- ICP 关闭: 通过索引查找所有 group_id = 100 的记录并回表校验 nickname 是否匹配
- ICP 开启: 在索引查找 group_id = 100 的记录时,同时校验 nickname 是否匹配
自适应哈希索引
自适应哈希索引(Adaptive Hash Index,简称 AHI)是MySQL InnoDB存储引擎引入的一项优化特性,用于提高特定查询模式下的性能。InnoDB 存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。
经验之谈
失效场景
- 索引列参与了运算
- 对索引列进行了隐式类型转换
- like '%value'
- where or
索引优化
- 尽量作用在区分度较高的列上
- 最好是走覆盖索引避免回表
- 长文本模糊查询可以加上全文索引
- 尽量选择占用存储空间较小的列
- 更新频繁的字段不适合添加索引