阅读内容关键字:索引底层结构与原理、索引失效分析、索引优化策略
1、索引
索引可以分为聚簇和非聚簇索引。
1.1、聚簇索引
一种对磁盘上实际数据重新组织以按指定的一个或多个列的值排序。别名有 簇类索引、聚集索引、聚类索引。
聚簇索引通过主键来形成 B 树结构,如果没有定义主键,则会以非空的唯一索引来替代,如果唯一索引也没有,则会定义一个隐式的主键来作为聚簇索引。主键也是聚簇索引的一种。
其中聚簇索引中,索引和整个数据一块存放,也就是索引即数据。它叶子节点存储的是索引结构和数据。大概的样子是这样。
什么是叶子节点:树中最底段的节点,叶子节点没有子节点。
1.1.1、主键索引
数据表的主键列使用的就是主键索引。
一张数据表有只能有一个主键,并且主键不能为 null,不能重复。
在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在null值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。
1.2、非聚簇索引
索引的一种。别名有 二级索引、辅助索引。
非聚簇索引中,索引的叶子节点指向的是主键的地址,所以如果根据二级索引来获取非索引数据的话,就需要先查找二级索引,然后得到主键索引,通过主键索引去查找具体的数据。同时,这个过程也叫回表操作。
1.2.1、唯一索引
唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。
1.2.2、普通索引
普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。
1.2.3、全文索引
全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。
1.2.4、空间索引
使用 spatial 参数可以设置索引为空间索引,空间索引只能建立在空间数据类型上比如 geometry,并且不能为空,目前只有 MyISAM 存储引擎支持。
1.2.5、组合索引
组合索引也算是非聚簇的一种,它和分别建索引的不同在于,组合索引只会有一颗索引树,而分别建索引就会有 n 颗索引树。
1.3、B 树存储记录数计算
如果是高度为 1 的 b tree,因为数据页默认大小是 16K,假设一行数据大概是 1K 占用空间,则一层可以存储 16 / 1 = 16 行数据。
如果高度为 2,又因为非叶子节点存储结构是 页指针 键值,我们假设主键 ID 为 int 类型,长度为4字节(byte),而指针大小在 InnoDB 源码中设置为 6 字节(byte),这样一共 10 字节(byte),那么一个页可以存放 16K / 10 B = 1638,叶子节点存放整条数据,而 1 层可以分裂 1638 个子节点,所以记录数是 1638 * 16 = 26208。
如果高度是 3 的话,可以分裂 1638 * 1638 个节点,然后再累计上叶子节点的记录数则是, 1638 * 1638 * 16 = 42,928,704,4 千多万数据。
高度 4:1638 * 1638 * 1638 * 16 = 70,317,217,152。七百多亿数据。
所以说 B 树的高度一般为 2 ~ 4 层。
tip:如果对记录的组成结构感兴趣可以看下 MySQL 的行格式。
2、索引策略
2.1、适合建立索引的 4 种情况
- 频繁在 where 条件中出现的字段。
- 经常 group by 与 order by 的字段
在生成索引树的过程中,数据会按照一定的顺序排列。所以建立索引之后,数据已经是有序状态,group by (先排序后分组)与 order by IO 次数减少,性能自然的就上去了。
- 多表连接时,on 的条件建议添加索引
- 建议使用散列值高的字段建立索引。
普通字段区分度计算:select COUNT(DISTINCT column_name)/COUNT(*) from table_name
前缀索引长度区分度计算:select COUNT(DISTINCT left(column_name,length))/COUNT(*) from table_name
2.2、不适合建立索引的几种情况
- where 中使用不到的字段
- 数据量小的表不建议建立索引
表数据量小时,走索引加载所有数据,不一定比全表扫描快。
- 有大量重复的数据不建议建立索引
走了索引发现索引当中只有两种类型的值,当匹配索引时就已经匹配一半的数据了,IO 也比较高,不建议建立索引。
- 经常更新的字段不建议建立索引
更新维护索引也是非常耗费性能的,毕竟索引就是前置操作,先拿出一部分时间为了加快后面的匹配速度,但是如果这个时间占比比较大时就得不偿失了。
- 不建议使用无序值作为索引
大量的无序值会造成页分裂,每次的 DML 操作都会造成索引的数据移动,非常耗费性能。
tips:组合索引优于单值索引名,相较于维护多个单值索引的索引树,组合索引只有一个索引树更具性能。
3、索引失效的 11 种情况
3.1、最左前缀匹配
组合索引条件匹配出现的位置必须严格按照组合索引的顺序出现。比如 (a,b,c) 组合索引,则匹配规则可以有以下情况:(a)、(a,b)、(a,b,c),当然由于优化器的原因,如果字段位置置换和原 SQL 是同样的意思,那么可以随意调换位置,优化器会进行重新排列,还原成索引的顺序。
当然还有一种情况是索引条件下推 (a,c) 也可能索引也是有效的。
3.2、匹配中存在运算、函数、类型转换
这种情况因为使用了未知的操作,不清楚方法会造成什么样的性能影响,所以优化器就放弃走索引了。
3.3、组合索引的范围查找后面的字段会失效
组合索引 (a,b,c)
代码语言:c#复制select * from table_name where a = 1 and b > 2 and c = 3
根据 explain 的 key_len 查看索引所占字节发现 c 这个索引没用上。
3.4、!= 索引会失效
3.5、is not null 索引会失效
is null 不会索引失效,所以建议字段默认值不要使用 null,使用其他值替换。
3.6、like 开头失效
like '%A' 会出现索引失效。
3.7、or 的前后存在非索引列
代码语言:c#复制select * from table_name where a = 1 or b = 2,这种情况就相当于 select * from table_name where a = 1 、select * from table_name where b = 2,b 是没有索引的,自然就索引失效了。
3.8、两张表的编码不一致 join 索引失效
如果两种表的编码不一致可能会发生函数的隐式转换,也就是出现了第二种情况,自然就索引失效。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!