页拆分(Page Splitting)工作原理及相关技术问题咨询
咱们一个个来聊这些页拆分的问题,都是数据库里很常见的核心知识点:
1. 什么是页拆分(Page Splitting)?它的工作机制是怎样的?
首先,数据库里的data page(数据页)是存储数据的最小物理单元,就像一个固定容量的小盒子,每个盒子能装的记录数是有限的。当你往一个已经装满(或者快装满,达到数据库设置的填充因子阈值)的页里插入新记录,或者更新现有记录导致它体积变大装不下时,就会触发页拆分。
具体运作逻辑很直白:
- 数据库会从磁盘上分配一个全新的空页;
- 把原页里大约一半的记录移到新页中;
- 更新原页和新页的关联指针,让它们在索引的逻辑链中保持连续;
- 最后把新插入/更新的记录放到合适的页里。
举个简单例子:假设一个页最多装4条记录,现在已经塞了4条,你插入第5条时,数据库就会把原页拆成两个页,每个页放2-3条记录,再把新记录放进对应的页。
2. 页拆分是否会影响索引或表中数据的物理排序?举例:若某带索引的邮件列频繁发生页拆分,邮件地址列表是否会失去原有排序?
这里要拆成逻辑排序和物理排序两个维度来看:
逻辑排序:完全不受影响
索引的逻辑顺序是由索引键值(比如邮件地址)严格维护的,页拆分后,数据库会自动更新索引的页链接,确保遍历索引时依然按照键值的有序性访问。比如你的邮件列索引是按字母顺序排的,不管怎么拆分页,查询返回的邮件地址列表依然是alice@xxx.com→bob@xxx.com→charlie@xxx.com的逻辑顺序。物理排序:可能变得不连续
原来的物理页可能是连续存放在磁盘上的,拆分后新页可能被分配到磁盘的其他位置,导致物理存储顺序和逻辑顺序不一致,但这只会轻微影响性能(比如磁盘随机读变多),对查询的逻辑结果没有任何影响。
回到你的例子:带索引的邮件列频繁拆分,邮件地址列表不会失去原有排序。比如原来一个页存alice到frank的邮件,插入george触发拆分后,原页留alice到charlie,新页存david到george,索引的逻辑链依然按字母串在一起,查询时还是会按字母顺序返回结果。
3. 当主键记录被删除时,页拆分/碎片是如何运作的?举例:假设有如下主键为数字的表:1, bob, chair;2, joe, table;3, brandon, lamp;4, jared, tv。假设删除2和3,会发生什么?
首先明确一个关键:删除记录不会触发页拆分,页拆分只有在插入/更新操作导致页空间不足时才会发生。删除操作带来的是碎片问题,主要分两种:
页内碎片(Internal Fragmentation)
当你删除页里的部分记录后,页内会出现未被利用的空闲空间。比如你的例子里,原来的页存了4条主键1-4的记录,删除2和3后,这个页里只剩下1和4两条数据,剩下的空间就是页内碎片。这时候页的空间利用率变低,但数据库不会马上回收这些空间——后续如果有新的记录(比如主键2重新插入,或者主键值在1-4之间的记录),会优先填充这个页的空闲空间。
页间碎片(External Fragmentation)
如果相邻的多个页都有较多空闲空间,数据库在后台维护任务(比如索引重建、统计信息更新)或者特定触发条件下,会执行页合并(Page Merging):把多个页的记录合并到同一个页里,然后释放多余的空页,以此减少碎片。
回到你的具体例子:
- 删除主键2和3后,存储这四条记录的页现在只剩1和4两条数据,产生明显的页内碎片;
- 这个过程不会触发页拆分,因为没有插入/更新导致空间不足的情况;
- 后续如果插入主键2的记录,数据库会把它放到该页的空闲空间里,保持主键的逻辑顺序(1→2→4);
- 如果插入主键5的记录,因为主键是递增的,数据库会把它放到新页(或者当前页还有剩余空间的话也会放进去),逻辑顺序依然是1→2→4→5。
内容的提问来源于stack exchange,提问作者jssteele89

