索引
索引(index)是帮助 MySQL 高效获取数据的数据结构,提高查询效率。
优点
- 提高数据的查询速度
- 降低数据库的 IO 成本
缺点
- 占用磁盘空间
- 增删改需要维护索引,性能可能会下降
索引的分类
|
|
主键索引
当一张表,把某个列设为主键的时候,则该列就是主键索引。
|
|
普通索引
用表中的普通列构建的索引,没有任何限制。
|
|
唯一索引
索引列的值必须唯一,允许有多个空值 null(和主键不同,主键不允许为空)。
|
|
全文索引
|
|
组合索引
组合索引 / 联合索引
|
|
聚簇索引
索引和数据放在一起,索引结构的叶子节点保存了行数据,找到了索引就找到了数据。
访问数据更快,聚簇索引将索引和数据保存在一个B+树上。
插入速度严重依赖于插入顺序。
更新聚簇索引列的代价很高。
非聚簇索引(二级索引)
将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键。
- 二级索引访问需要两次索引查找,要回表。
索引的结构
B Tree
B 树是一种多叉平衡查找树,其主要特点:
树的节点中存储着多个元素,每个内节点有多个分叉。
在所有的节点都储存数据。每个节点中包含键值和数据,节点中的键值从小到大排列。
父节点当中的元素不会出现在子节点中。
所有的叶子结点都位于同一层,叶节点具有相同的深度,叶节点之间没有指针连接。
B+ Tree
B+ 树是 B 树的升级版。B+ 树在结构上和B树的区别在于:
只有叶子节点才会存储数据,非叶子节点只存储键值,用于查找。
叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。
叶子节点保存了父节点所有关键字记录的指针。
相比与 B 树,B+ 树有以下提升:
扫表能力更强(进行全表扫描,只需要遍历叶子节点即可)
具有更好的磁盘读写能力(非叶子节点不存储数据,使树更矮,IO 操作更少,一次磁盘加载扫描范围更大)
查询效率稳定(数据都存储在叶子节点,IO 次数稳定)
增删数据效率更高(数据在叶子节点以有序链表存储,可提高数据增删效率)
数据库中,B+ 树的高度一般都在 2~4 层。由于 MySQL 的 INNODB 引擎在设计时是将 B+ 树的根节点常驻内存的,因此在查找某一键值的行记录时,最多只需要 1~3 次磁盘IO。
Hash
支持 hash 索引的是 Memory 存储引擎。采用一定的 hash 算法,将键值换算成新的 hash 值,映射到对应的槽位上,然后存储在 hash 表中。
特点:
- 只能用于对等比较(=, in),不支持范围查询(between, >, <)
- 无法利用索引完成排序操作
- 查询效率高,通常只需要一次检索(无 hash 冲突的情况)
创建索引的原则
🙆 建议创建索引
- ✅
select
语句,频繁作为where
条件的字段
|
|
- ✅
update/delete
语句的where
条件
|
|
- ✅ 需要分组,排序的字段
|
|
- ✅ 使用
distinct
的字段
|
|
✅ 字段需要唯一性约束
- 唯一索引
- 主键索引
✅ 多表查询,连接字段应创建索引
- 类型务必保持一致,避免隐式装换
|
|
🙅 不建议创建索引
❌
where
子句中用不到的字段索引的作用是快速定位到想要的数据,用不到就没有必要创建数据了。
❌ 表里的记录非常少
如只有 100 条数据,有无影响不大。
❌ 有大量重复数据,选择性低
如:性别字段,导致大量回表。
索引的选择性越高,查询效率越好,可以在查找过程中过滤更多的行
❌ 频繁更新的字段,创建索引要考虑维护索引的开销
频繁修改的字段,不推荐创建索引。
索引失效与解决方案
是否使用索引取决于 🚀 查询代价
即使可以使用索引时,但 MySQL 评估后,不如全表扫描,会放弃使用索引
⚠️ 索引列不独立,进行了计算或变成了参数
- 索引字段进行了表达式计算
|
|
应事先计算好表达式的值,再传入,避免在 SQL where
的左侧做计算。
- 索引字段是函数的参数
|
|
事先计算好再传入。
或者使用一些等价的 SQL。
|
|
⚠️ 使用了左模糊
|
|
如果无法避免,且有较高需求,可以考虑使用搜索引擎。
⚠️ or 查询的部分字段没有索引
or 的含义是两个条件满足一个即可,所以只有一个条件列有索引是无用的。只要条件列中有一个不是索引列,就会进行全表扫描。
|
|
解决:根据实际业务,添加相应的索引。
添加索引后,避免了全表扫描。
⚠️ 不符合最左前缀原则的查询
|
|
|
|
|
|
⚠️ 字符串条件没有使用 ""
|
|
⚠️ 隐式转换导致索引失效
|
|
null 条件查询
查询时,采用
is null
is not null
条件,根据表中的数据分布,MySQL 会自行判断是否走索引,并不固定MySQL 官方建议尽量把字段定义为
NOT NULL
|
|
MySQL 评估全表扫描更快
MySQL 在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。
因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。
最左前缀法则
最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。
如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。
中间不能跳过某一列,否则该列后面的字段索引将失效。
范围查询
联合索引中,出现范围查询(>, <),范围查询右侧的列索引失效。
覆盖索引
对于索引 X,SELECT
的字段只需要从索引中就能获得,而无需回表获取。
|
|
前缀索引
当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让 索引变得很大,查询时,浪费大量的磁盘 IO, 影响查询效率。
可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
|
|
explain
查看 SQL 语句的执行计划。字段含义:
id
- 在一个大的查询语句中,每个 select 关键字都对应一个唯一 id
- id 相同,执行顺序从上到下;id 不同,值越大,越先执行
select_type
- 表示 select 对应的查询类型
- SIMPLE (简单 select,不使用 union 或子查询等)
- PRIMARY (查询中若包含任何复杂的子部分,最外层的 select 被标记为 PRIMARY)
- UNION (union 中的第二个或后面的 select 语句)
- SUBQUERY (子查询中的第一个 select)
table
- 这一行的数据是关于哪张表
type
- MySQL 在表中找到所需行的方式
- 性能由好到差的连接类型为:NULL、system、const、eq_ref)、ref、range、index、all(全表扫描)
possible_keys
- 可能应用在这张表上的索引
key
- 实际使用的索引,如果为 NULL,则没有使用索引
key_len
- 实际使用索引的长度(该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好)
ref
- 上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
rows
- MySQL 认为必须要执行查询的行数,在 innodb 引擎的表中,是一个估计值,不是准确的
Extra
- MySQL解决查询的详细信息