浅谈B+树索引的分裂优化(转)
http://www.tamabc.com/article/85038.html
从MySQL Bug#67718浅谈B+树索引的分裂优化
原文链接:http://hedengcheng.com/?p=525
问题背景
今天,看到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对应的问题:
InnoDB的索引分裂策略,在特定的情况下,索引页面的分裂存在问题,导致每个分裂出来的页面,仅仅存储一条记录,页面的空间利用率极低。
此Bug引起了我的兴趣,因此准备跟大家简单聊聊B+树索引的结构、B+树的分裂、B+树分裂操作的优化、Bug #67718的成因,以及个人对如何修复此Bug的一些建议等。
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+树结构会产生以下的一系列变化:
插入记录6,新的B+树结构如下:
![]()
插入记录7,由于叶页面中只能存放4条记录,插入记录7,导致叶页面分裂,产生一个新的叶页面。
![]()
传统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的问题。
浅谈B+树索引的分裂优化(转)的更多相关文章
- 从MySQL Bug#67718浅谈B+树索引的分裂优化(转)
原文链接:http://hedengcheng.com/?p=525 问题背景 今天,看到Twitter的DBA团队发布了其最新的MySQL分支:Changes in Twitter MySQL 5. ...
- MySQL Bug#67718 浅谈B+树索引的分裂优化
原文链接:http://hedengcheng.com/?p=525 问题背景 今天,看到Twitter的DBA团队发布了其最新的MySQL分支:Changes in Twitter MySQL 5. ...
- 浅谈oracle树状结构层级查询之start with ....connect by prior、level及order by
浅谈oracle树状结构层级查询 oracle树状结构查询即层次递归查询,是sql语句经常用到的,在实际开发中组织结构实现及其层次化实现功能也是经常遇到的,虽然我是一个java程序开发者,我一直觉得只 ...
- 前端性能优化--为什么DOM操作慢? 浅谈DOM的操作以及性能优化问题-重绘重排 为什么要减少DOM操作 为什么要减少操作DOM
前端性能优化--为什么DOM操作慢? 作为一个前端,不能不考虑性能问题.对于大多数前端来说,性能优化的方法可能包括以下这些: 减少HTTP请求(合并css.js,雪碧图/base64图片) 压缩( ...
- InnoDB 中 B+ 树索引的分裂
数据库中B+树索引的分裂并不总是从页的中间记录开始,这样可能会导致空间的浪费,例如下面的记录: 1, 2, 3, 4, 5, 6, 7, 8, 9 插入式根据自增顺序进行的,若这时插入10这条记录后需 ...
- 浅谈oracle树状结构层级查询测试数据
浅谈oracle树状结构层级查询 oracle树状结构查询即层次递归查询,是sql语句经常用到的,在实际开发中组织结构实现及其层次化实现功能也是经常遇到的,虽然我是一个java程序开发者,我一直觉得只 ...
- (转)浅谈trie树
浅谈Trie树(字典树) Trie树(字典树) 一.引入 字典是干啥的?查找字的. 字典树自然也是起查找作用的.查找的是啥?单词. 看以下几个题: 1.给出n个单词和m个询问,每次询问 ...
- 【转】Senior Data Structure · 浅谈线段树(Segment Tree)
本文章转自洛谷 原作者: _皎月半洒花 一.简介线段树 ps: _此处以询问区间和为例.实际上线段树可以处理很多符合结合律的操作.(比如说加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[ ...
- 浅谈Android样式开发之布局优化
引言 今天我们来谈一下Android中布局优化常用的一些手段.官方给出了3种优化方案,分别是</include>.</viewstub>.</merge>标签,下面 ...
随机推荐
- 抛弃jQuery 深入原生的JavaScript
虽然我已经做网站建设工作10多年了,但我从最近3年才开始更多地学习如何更好的将纯JavaScript用于工作中,而不总是将jQuery考虑在第一位.现在我每天学习很多东西.这个过程让我觉得Adtile ...
- 《Thinking in C++》学习笔记(一)【第二章】
第二章 对象的创建与使用 2.1语言的翻译过程 翻译器分为两类:解释器(interpreter)和编译器(compiler). 2.1.1解释器 解释器将源代码转化成一些动作(它可由许多机器指令组成) ...
- Nginx状态监控
通过配置nginx.conf文件来实现对Nginx状态信息的监控. 1.配置nginx.conf vim /usr/local/nginx/conf/nginx.conf 再server块配置项中添加 ...
- spring_150803_component
实体类: package com.spring.model; public class DogPet { private int id; private String name; private in ...
- 【android原生应用】之闹钟应用搭起篇
由于工作原因接触android开发一段时间了,对于开发有了一些了解,于是萌生了搭起android原生应用进行分析和学习的想法.先从闹钟应用开始吧. 1.首先要下载原生应用,原生应用在原生系统里面(当然 ...
- I,P,B帧和PTS,DTS的关系
http://www.cnblogs.com/qingquan/archive/2011/07/27/2118967.html 基本概念: I frame :帧内编码帧 又称intra picture ...
- Linux基础--文件压缩
1.compress [root@linux ~]# compress [-dcr] 档案或目录 参数: -d:用来解压缩的参数 -r:可以连同目录下的档案也同时给予压缩呢! -c:将压缩数据输出成为 ...
- 【web性能】web性能测试工具推荐
WEB性能测试工具主要分为三种,一种是测试页面资源加载速度的,一种是测试页面加载完毕后页面呈现.JS操作速度的,还有一种是总体上对页面进行评价分析,下面分别对这些工具进行介绍,如果谁有更好的工具也请一 ...
- POJ1088滑雪
Description Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道 ...
- Model的绑定
ReflectedControllerDescriptor:ControllerDescriptor Controller的 public override ActionDescriptor Find ...