原理先行,再实践验证!

索引的常见模型

索引实现 适用范围 缺点
哈希索引 适用于只有等值查询的场景 区间查询速度很慢
有序数组 在等值查询和范围查询场景中的性能就都非常优秀 更新操作成本太高
搜索树 B+ 树能够很好地配合磁盘的读写特性,减少单次查询的磁盘访问次数。

InnoDB的索引模型

image-20210723223213434

InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。

在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。

  • 聚簇索引以INNODB为例,叶子结点即存储了真实的数据行,不再有另外单独的数据页。
  • 非聚簇索引以MYISAM为例,是按列值与行号来组织索引的,它的叶子节点中保存的实际上是指向存放数据的物理块的指针。

非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。

一张表可以有多个树,树的每个节点对应一个page,一个page里面可能有多个数据块,因为page的大小是16KB,如果key 的大小是8字节,指针的大小是6字节,那么一个节点会产生16KB/(8+6)字节=1170个分支。

image-20210723223641353

根据叶子节点的内容,索引类型分为主键索引和非主键索引:

  • 主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index);
  • 非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。

image-20210723224009306

image-20210723224555731

image-20210724065454480

image-20210724065745937

image-20210724070118020

基于主键索引和普通索引的查询有什么区别?

  • 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
  • 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。

索引维护

img

B+ 树为了维护索引有序性,在插入新值的时候需要做必要的维护。以上面这个图为例,有三种情景:

  • 如果插入新的行 ID 值为 700,则只需要在 R5 的记录后面插入一个新记录;
  • 如果新插入的 ID 值为 400,需要逻辑上挪动后面的数据,空出位置;
  • 如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂

除了影响性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,整体空间利用率降低大约 50%。

自增主键的插入数据模式,每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。

另外,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小,单个节点存放的指针也就越多。

联合索引(A, B)意味着不需要建立A的索引了,因为这个联合索引意味着建立了(A,B)和(A)这两种索引。

如果既有联合查询,又有基于 a、b 各自的查询呢?查询条件里面只有 b 的语句,是无法使用 (a,b) 这个联合索引的,这时候你不得不维护另外一个索引,也就是说你需要同时维护 (a,b)、(b) 这两个索引。

用不上最左前缀,如果是高频的查询,就再建一个单独的索引,当然我们可以选择交换顺序,争取建立的索引占用的内存越小,比如name 字段是比 age 字段大的 ,那我就建议你创建一个(name,age) 的联合索引和一个 (age) 的单字段索引。

为什么要重建索引?

因为索引可能因为删除,或者页分裂等原因,导致数据页有空洞,重建索引的过程会创建一个新的索引,把数据按顺序插入,这样页面的利用率最高,也就是索引更紧凑、更省空间。

InnoDB 引擎,虽然删除了表的部分记录,但是它的索引还在, 并未释放。

1.select * from T where k in(1,2,3,4,5)
2.select * from T where k between 1 and 5

第一个要树搜素5次
第二个搜索一次

因为覆盖索引的目的就是”不回表“,所以只有索引包含了where条件部分和select返回部分的所有字段,才能实现这个目的。

查询语句的where里面各个判断调换顺序没关系的,优化器会自动处理。

一次查询为啥不能只回表一次呢?比如范围查询通过二级索引定位到全部符合条件的主键再回表一次??

这样还需要地方把”满足条件的所有主键值“存起来,就要用到临时表了,性能反而更差


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Git必知必会 Previous
MySQL8.0.25安装 Next