大白话介绍
想象你有一个书架,专门用来按字母顺序存放书本。当你获得一本新书并尝试放入适当的位置时,你发现那个位置已经满了,无法再放下任何书本。这时,你会怎么办?一个好方法是拿出一个新的空架子,并从原来的位置上移动一些书本到新架子上,确保两个架子上的书本都有足够的空间,并且新书也能被顺利放入。
在这个过程中,你会尽量均等地分配两个架子上的书本数量,这样无论你想找哪本书,查找起来都会比较容易。如果将来你继续获得更多的书本,可能还需要进行更多类似的调整。
在MySQL的InnoDB存储引擎中,书架就像是“页”,书本就像是数据记录。当一个页已经满了,但我们还需要添加更多的数据时,InnoDB会执行一个叫做“页分裂”的过程。这个过程实际上是创建一个新的页,并将一些数据从原页移动到新页中,从而使得数据存储既高效又有序。这保证了无论是进行数据插入、查询还是更新操作,数据库的性能都能得到优化。
页分裂流程介绍
-
插入前的检查:
- 当一个插入操作被发起,首先,InnoDB会定位到要插入记录的位置。这通常涉及到B+树索引的搜索。
- 在找到插入点后,InnoDB检查目标页是否有足够空间容纳新记录。这一步是通过比较页内剩余空间和新记录大小来完成的。
-
判断是否需要页分裂:
- 如果页内有足够空间,则直接插入记录,无需页分裂。
- 如果没有足够空间,InnoDB决定进行页分裂。
-
执行页分裂:
- 创建新页:系统分配一个新的页,这个页的数据结构与被分裂的页相同。
- 记录的迁移和分配:选择一部分记录从原页迁移到新页。InnoDB试图平均分配两个页的空间利用,同时保持B+树的顺序性不变。
- 插入新记录:根据新记录的键值决定它是插入到原页还是新页中。
- 调整B+树索引:页分裂可能导致父节点的键值需要更新,以反映新页的加入。如果父节点没有足够空间,这个过程可能递归到更高层次的节点,甚至到根节点,可能导致树的高度增加。
-
更新系统元数据和日志:
- 元数据更新:包括页的元数据,如页链表、空闲列表等。
- 写入重做日志(Redo Log):每次页分裂操作都会记录在重做日志中,确保在系统故障时可以恢复数据。
案例介绍
A. 主键顺序插入效果
①. 从磁盘中申请页, 主键顺序插入
②. 第一个页没有满,继续往第一页插入
③. 当第一个也写满之后,再写入第二个页,页与页之间会通过指针连接、
④. 当第二页写满了,再往第三页写入
B. 主键乱序插入效果
①. 加入1#,2#页都已经写满了,存放了如图所示的数据
②. 此时再插入id为50的记录
47所在的1#页,已经写满了,存储不了50对应的数据了。 那么此时会开辟一个新的页 3#。
但是并不会直接将50存入3#页,而是会将1#页后一半的数据,移动到3#页,然后在3#页,插入50。
移动数据,并插入id为50的数据之后,那么此时,这三个页之间的数据顺序是有问题的。 1#的下一个页,应该是3#, 3#的下一个页是2#。 所以,此时,需要重新设置链表指针。
上述的这种现象,称之为 "页分裂",是比较耗费性能的操作。
案例介绍参考:黑马数据库教程