MySQL 索引原理与优化

阅读内容关键字:索引底层结构与原理、索引失效分析、索引优化策略

1、索引

索引可以分为聚簇和非聚簇索引。

1.1、聚簇索引

一种对磁盘上实际数据重新组织以按指定的一个或多个列的值排序。别名有 簇类索引、聚集索引、聚类索引。

聚簇索引通过主键来形成 B+ 树结构,如果没有定义主键,则会以非空的唯一索引来替代,如果唯一索引也没有,则会定义一个隐式的主键来作为聚簇索引。主键也是聚簇索引的一种。

其中聚簇索引中,索引和整个数据一块存放,也就是索引即数据。它叶子节点存储的是索引结构和数据。大概的样子是这样。

主键索引.png

什么是叶子节点:树中最底段的节点,叶子节点没有子节点。

1.1.1、主键索引

数据表的主键列使用的就是主键索引。

一张数据表有只能有一个主键,并且主键不能为 null,不能重复。

在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在null值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。

1.2、非聚簇索引

索引的一种。别名有 二级索引、辅助索引。

非聚簇索引中,索引的叶子节点指向的是主键的地址,所以如果根据二级索引来获取非索引数据的话,就需要先查找二级索引,然后得到主键索引,通过主键索引去查找具体的数据。同时,这个过程也叫回表操作。

1.2.1、唯一索引

唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。

1.2.2、普通索引

普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。

普通索引.png

1.2.3、全文索引

全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。

1.2.4、空间索引

使用 spatial 参数可以设置索引为空间索引,空间索引只能建立在空间数据类型上比如 geometry,并且不能为空,目前只有 MyISAM 存储引擎支持。

1.2.5、组合索引

组合索引也算是非聚簇的一种,它和分别建索引的不同在于,组合索引只会有一颗索引树,而分别建索引就会有 n 颗索引树。

组合索引.png

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 种情况

  1. 频繁在 where 条件中出现的字段。

  2. 经常 group by 与 order by 的字段

在生成索引树的过程中,数据会按照一定的顺序排列。所以建立索引之后,数据已经是有序状态,group by (先排序后分组)与 order by IO 次数减少,性能自然的就上去了。

  1. 多表连接时,on 的条件建议添加索引

  2. 建议使用散列值高的字段建立索引。

普通字段区分度计算:select COUNT(DISTINCT column_name)/COUNT(*) from table_name

前缀索引长度区分度计算:select COUNT(DISTINCT left(column_name,length))/COUNT(*) from table_name

2.2、不适合建立索引的几种情况

  1. where 中使用不到的字段

  2. 数据量小的表不建议建立索引

表数据量小时,走索引加载所有数据,不一定比全表扫描快。

  1. 有大量重复的数据不建议建立索引

走了索引发现索引当中只有两种类型的值,当匹配索引时就已经匹配一半的数据了,IO 也比较高,不建议建立索引。

  1. 经常更新的字段不建议建立索引

更新维护索引也是非常耗费性能的,毕竟索引就是前置操作,先拿出一部分时间为了加快后面的匹配速度,但是如果这个时间占比比较大时就得不偿失了。

  1. 不建议使用无序值作为索引

大量的无序值会造成页分裂,每次的 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)

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 的前后存在非索引列

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 索引失效

如果两种表的编码不一致可能会发生函数的隐式转换,也就是出现了第二种情况,自然就索引失效。

猜你喜欢

转载自juejin.im/post/7110594021117919263