ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

从MySQL Bug#67718浅谈B+树索引的分裂优化

2020-08-12 21:31:56  阅读:340  来源: 互联网

标签:浅谈 67718 记录 插入 分裂 索引 MySQL Bug 页面


问题背景

今天,看到Twitter的DBA团队发布了其最新的MySQL分支:Changes in Twitter MySQL 5.5.28.t9,此分支最重要的一个改进,就是修复了MySQL 的Bug #67718:InnoDB drastically under-fills pages in certain conditions。关于此Bug的详细描述,以及如何重现此问题,能够阅读以上的Bug连接,如下简单描述下此Bug对应的问题:html

 

InnoDB的索引分裂策略,在特定的状况下,索引页面的分裂存在问题,致使每一个分裂出来的页面,仅仅存储一条记录,页面的空间利用率极低。
mysql

 

此Bug引发了个人兴趣,所以准备跟你们简单聊聊B+树索引的结构、B+树的分裂、B+树分裂操做的优化、Bug #67718的成因,以及我的对如何修复此Bug的一些建议等。sql

 

 

B+树索引结构

传统关系型数据库(Oracle/MySQL/PostgreSQL…),其主要的索引结构,使用的都是B+树。更有甚者,InnoDB引擎的表数据,整个都是以B+树的组织形式存放的。下图,是一个经典的B+树组织结构图(2层B+树,每一个页面的扇出为4):数据库

 

注意:优化

  • 此B+树,以InnoDB实现的B+树结构为准;
  • 此B+树,有5条用户记录,分别是1,2,3,4,5;
  • B+树上层页面中的记录,存储的是下层页面中的最小值(Low Key);
  • B+树的全部数据,均存储在B+树的叶节点;
  • B+树叶节点的全部页面,经过双向链表连接起来;

 

B+树的分裂

在上图B+树的基础上,继续插入记录6,7,B+树结构会产生如下的一系列变化:3d

插入记录6,新的B+树结构以下:htm

 

插入记录7,因为叶页面中只能存放4条记录,插入记录7,致使叶页面分裂,产生一个新的叶页面。blog

 

传统B+树页面分裂操做分析:索引

  • 按照原页面中50%的数据量进行分裂,针对当前这个分裂操做,3,4记录保留在原有页面,5,6记录,移动到新的页面。最后将新纪录7插入到新的页面中;
  • 50%分裂策略的优点:
    • 分裂以后,两个页面的空间利用率是同样的;若是新的插入是随机在两个页面中挑选进行,那么下一次分裂的操做就会更晚触发;
  • 50%分裂策略的劣势:
    • 空间利用率不高:按照传统50%的页面分裂策略,索引页面的空间利用率在50%左右;
    • 分裂频率较大:针对如上所示的递增插入(递减插入),每新插入两条记录,就会致使最右的叶页面再次发生分裂;

 

疑问:

传统50%分裂的策略,有不足之处,如何优化?接着往下看。

 

B+树分裂操做的优化

因为传统50%分裂的策略,有不足之处,所以,目前全部的关系型数据库,包括Oracle/InnoDB/PostgreSQL,以及本人之前参与研发的Oscar数据库,目前正在研发的NTSE、TNT存储引擎,都针对B+树索引的递增/递减插入进行了优化。通过优化,以上的B+树索引,在记录6插入完毕,记录7插入引发分裂以后,新的B+树结构以下图所示:

 

对比上下两个插入记录7以后,B+树索引的结构图,能够发现两者有不少的不一样之处:

  • 新的分裂策略,在插入7时,不移动原有页面的任何记录,只是将新插入的记录7写到新页面之中;
  • 原有页面的利用率,仍旧是100%;
  • 优化分裂策略的优点:
    • 索引分裂的代价小:不须要移动记录;
    • 索引分裂的几率下降:若是接下来的插入,仍旧是递增插入,那么须要插入4条记录,才能再次引发页面的分裂。相对于50%分裂策略,分裂的几率下降了一半;
    • 索引页面的空间利用率提升:新的分裂策略,可以保证分裂前的页面,仍旧保持100%的利用率,提升了索引的空间利用率;
  • 优化分裂策略的劣势:
    • 若是新的插入,再也不知足递增插入的条件,而是插入到原有页面,那么就会致使原有页面再次分裂,增长了分裂的几率。

 

所以,此优化分裂策略,仅仅是针对递增递减插入有效,针对随机插入,就失去了优化的意义,反而带来了更高的分裂几率。

 

在InnoDB的实现中,为每一个索引页面维护了一个上次插入的位置,以及上次的插入是递增/递减的标识。根据这些信息,InnoDB可以判断出新插入到页面中的记录,是否仍旧知足递增/递减的约束,若知足约束,则采用优化后的分裂策略;若不知足约束,则退回到50%的分裂策略。

 

可是,InnoDB的实现,有不足之处,会致使下面提到的一个Bug。

 

Bug#67718的成因

在Bug#67718中提到,在特定的插入状况下,InnoDB的索引页面利用率极低,这是因为InnoDB不正确的使用优化分裂策略致使的。

考虑如下的一个B+树,已有的用户数据是1,2,3,4,5,6,100,而且在插入记录100以后,引发索引页面分裂,记录100在分裂后被插入到新的页面:

 

因为插入100可以知足递增的判断条件,所以采用了优化分裂策略,分裂不移动数据,新纪录100插入到新页面之中,原有页面的最后插入位置仍旧是6号记录不变,原有页面仍旧保持递增的插入标识不变。

此时,考虑连续插入9,8,7这几条记录,会获得什么样的B+树?此时,全局递增插入变为全局递减插入。

插入记录9后的B+树结构:

因为InnoDB的B+树,上层节点保存的是下层页面中的最小值(Low Key),所以记录9仍旧会插入到【3,4,5,6】页面,此时页面已满,须要分裂。并且判断出记录9仍旧知足页面中的递增判断条件(Last_Insert_Pos = 6,9插入到6以后,而且原来是递增插入的)。所以,采用优化的分裂策略,产生新的页面插入记录9,原有页面记录保持不变。

 

插入记录8后的B+树结构:

 

插入记录7,也同样。采用优化的分裂策略,记录7独占一个页面。

 

分析:

  • Bug#67718的主要反作用
    • 是页面的利用率极低,每一个索引叶页面,只能存放一条记录;
  • Bug#67718的主要缘由
    • InnoDB错误的采用了优化的索引分裂策略。InnoDB判断是否知足递增/递减的插入模式,采用的是页面级的判断,哪怕全局的模式发生了变化,只要页面内记录的模式未变,仍旧会选择优化后的索引分裂策略;  

修复Bug#67718的建议

在本人作Oscar数据库的索引分裂优化时,当时也一样碰到了此问题。当时的解决方案是:每次分裂,若插入的记录是页面中的最后一条记录,则至少将此记录前一条记录分裂到新页面之中。采用此策略,针对100,9,8这一个系列的插入,会产生如下的系列B+树:

插入100,9,8后的B+树:

 

插入100时,移动原有页面最后一条记录到新的页面(将6移动到新页面),此时新页面中的记录为【6,100】。接下来插入9,8,都会插入到新的页面之中,不会产生分裂操做,空间利用率提升,减小了索引页面分裂,解决了Bug#67718的问题。

标签:浅谈,67718,记录,插入,分裂,索引,MySQL,Bug,页面
来源: https://www.cnblogs.com/mscm/p/13493129.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有