原理先行,再实践验证!
索引的常见模型
索引实现 | 适用范围 | 缺点 |
---|---|---|
哈希索引 | 适用于只有等值查询的场景 | 区间查询速度很慢 |
有序数组 | 在等值查询和范围查询场景中的性能就都非常优秀 | 更新操作成本太高 |
搜索树 | B+ 树能够很好地配合磁盘的读写特性,减少单次查询的磁盘访问次数。 |
InnoDB的索引模型
InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。
在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
- 聚簇索引以INNODB为例,叶子结点即存储了真实的数据行,不再有另外单独的数据页。
- 非聚簇索引以MYISAM为例,是按列值与行号来组织索引的,它的叶子节点中保存的实际上是指向存放数据的物理块的指针。
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。
一张表可以有多个树,树的每个节点对应一个page,一个page里面可能有多个数据块,因为page的大小是16KB,如果key 的大小是8字节,指针的大小是6字节,那么一个节点会产生16KB/(8+6)字节=1170个分支。
根据叶子节点的内容,索引类型分为主键索引和非主键索引:
- 主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index);
- 非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。
基于主键索引和普通索引的查询有什么区别?
- 如果语句是 select * from T where ID=500,即主键查询方式,则只需要搜索 ID 这棵 B+ 树;
- 如果语句是 select * from T where k=5,即普通索引查询方式,则需要先搜索 k 索引树,得到 ID 的值为 500,再到 ID 索引树搜索一次。这个过程称为回表。
索引维护
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 协议 ,转载请注明出处!