上一篇 【B+树索引】索引页的结构含有可以快速查询的秘密 从索引页的角度认识了MySQL为了提升查询速率,使用了 B+ 树的数据结构对索引页进行了内存存储。以非叶子节点为目录项结构,叶子节点为用户记录。下面咱聊聊 InnoDB 中 B+ 树的索引的注意事项。(总结自《MySQL是怎样运行的》)
首先得明确一点是:聚簇索引不是咱创建的,它默认就在那的。刚开始就一个节点,那就是根节点,一个空的页。
随后有用户记录插进来了,就往这根节点的 User Records
中插入用户记录。
随着用户记录不断地增多, Free Space 部分空间逐渐减少直至为 0.
然后再插记录的话,由于根节点已经满了,此时就会发生页分裂。此时会寻一个页 a 将根节点的用户记录都复制到 页 a,再取一个页 b 存放新插入的记录。然后根节点现在即是目录项节点了,其用户记录就是主键值,映射的是页a和页b。如此往复…
此过程就是为了说明 B+ 树的根节点就是那一个,不会变,其页号不会改变。
就是说,二级索引也不一定会设置唯一和非空的约束的,对于二级索引记录来说,是先按二级索引列的值进行排序,在二级索引列值相同的情况下,再按照主键值进行排序。插入数据时是需要往 B+ 树中插入记录的,当二级索引出现多个相同的时,此时该如何插入呢?索引引入了主键,当二级索引相同的时候,根据主键的升序查找需要插入的位置。
其实这个最不难理解的,假设一个页只能容纳一条记录,那引入 B+ 树的意义又何在呢?直接查叶子节点不就好了,还费半天查目录项索引页,闲呀。
这是为什么存储可变字段,限制一个列最多存储字节的原因,然后让溢出字段存放在另一个页。这也是为什么我看很多表的创建会去使用 Dynamic
行格式的原因,如果发生溢出了直接把所有记录放到另一个页里,然后存的是那页的页号(或者说地址),我觉得就极大减少了容量,然后可以存更多的记录,当然其实 MySQL 设计者在省空间这一块算极致了。
回表的概念:就是说当使用二级索引的时候,无法从二级索引中获取查询的全部数据,得从二级索引中得到主键值然后再从聚簇索引中查找所需数据,这一个需要回到聚簇索引的过程叫做回表。
需要执行回表操作的记录越多,使用二级索引进行查询的效率也就越低,某些查询中宁愿使用全表扫描也不适用二级索引。比如:假设 key1 值在 ‘a’~‘c’ 之间的用户记录数量占所有记录的大多数,如果使用 key1 这个字段对应的二级索引,则会有大量的 id 值需要进行回表操作,这还不如直接进行全表扫描呢。
也就是说需要执行回表的操作越多,就会越倾向于使用全表扫描,反之则倾向于使用二级索引+回表的方式。而 limit 可以限制查询的记录数,这一操作可能使查询更倾向于二级索引+回表的方式。
对于需要对结果进行排序的查询,如果在采用二级索引执行查询时需要执行回表操作的记录特别多,也倾向于使用全表扫描 + 文件排序的方式进行排序。比如下面的这个查询语句:
select * from single_table order by key1
由于查询的列表是 * ,如果使用二级索引+回表的方式进行排序,则需要对所有的二级索引记录执行回表操作。这种操作的成本还不如直接遍历聚簇索引然后再进行文件排序低,所以查询优化器会倾向于使用全表扫描的方式执行查询,然后对文本进行排序。
但是如果添加了limit
关键字,如下:
select * from single_table order by key1 limit 10
这个查询需要执行的回表操作的记录特别少,查询优化器就会倾向于使用 二级索引 + 回表的方式来执行。
alter table single_table add index idx_key1(10));
B+ 树索引在空间和时间上都有代价,所以没事儿别瞎建立索引。
空间:B+ 树索引的建立,占内存的。
时间:当执行增删操作的时候生成执行计划,而且还要给相关的二级索引进行增删,时间上也得付出代价滴。
参考:
《MySQL是怎样运行的》
题外话,一个疑问答案:为什么有了 binlog
记录操作日志,InnoDB 还要引入个 redolog
记录操作日志呢?
这还是和俩日志的作用有关系,binlog 是在 Server 层进行工作,记录的是逻辑操作,用于数据库的复制和恢复操作。而 redolog 的存在是在 InnoDB 存储引擎上工作的,它的存在是为了保障数据库的一致性和持久性。它记录的是物理操作,它在事务提交后会刷新到磁盘上。即使数据库崩溃了,redolog 也会进行数据恢复。