并发写Btree原理剖析
OceanBase 0.4的UpdateServer存储引擎使用了一棵可以多线程并发修改的BTree,读写不冲突,由颜然 开发。线上运行稳定。
UpdateServer存储引擎采用类leveldb的方式,最近的更新操作都写入内存中的一个活跃memtable,当活跃memtable占用内存达到某个阈值时,即冻结,dump到磁盘上形成sstable,从而释放内存。然后会开启一个新的memtable供随后的写入。与leveldb不同的是,leveldb采用skiplist做为核心存储数据结构,而UpdateServer采用B+Tree(实际上,和B+ Tree不一样,叶子节点没有通过链表连起来)。
从需求上看,数据结构不需要支持物理删除,只需要在上层支持标记删除。翻看leveldb的skiplist,它也不支持删除,在不支持删除的情况下,skiplist很容易做到读写不冲突,只需要利用一些memory barrier。写写冲突还是有,需要外部进行同步。
同理,BTree也不支持删除, 支持插入更新,查询,节点可以分裂,不能合并。写操作基于读写锁+Copy On Write,读操作不用加锁,Copy On Write产生的老节点不立即释放,以保证读操作继续能读到老节点。
写操作流程
1. 从树根节点开始到达叶子节点,对所有的节点加上共享锁
2. 将叶子节点的共享锁升级为排他锁(锁结构用qlock表示),只有一个写线程可以升级成功,升级失败的写线程将搜索路径上所有节点的共享锁释放。
3. 升级成功的写线程检查叶子节点是否满,分两条路径:
3.1 如果不满:
3.1.1 将叶子节点A拷贝一份出来,新叶子节点记作B,然后往B中写入Key/Value对
3.1.2 修改父亲节点指针指向新的叶子节点B(有可能别的写线程由于分裂这个父亲节点的其他叶子节点导致需要分裂这个父亲节点,为了防止这种并发修改,分裂节点前需要加排他锁,修改指针需要加共享锁)
3.2 如果叶子节点满:
3.2.1 分配两个叶子节点记作C和D,将老叶子节点A的左半放入Copy到C,右半Copy到D中,同时插入输入Key/Value
3.2.2 升级父亲节点的共享锁为排他锁(如果两个写线程同时发现父亲节点需要分裂,那么只有一个写线程能够升级排他锁成功,升级失败的写线程会将自己已持有的
排他锁降级为共享锁,然后将所有的持有的共享锁都释放)
3.2.3 往父亲节点中put一个新的指针,以指向分裂后的新的叶子节点,这个put操作同时也需要递归的处理父亲节点分裂
3.2.4 put成功后,将当前节点的所有上层节点的共享锁从上往下依次释放,然后释放当前节点的排他锁。
5. 将叶子节点A加入到写操作的输入参数recycle_node中,写操作过程中所有的通过Copy On Write而遗留的老叶子节点都会加入进去,recycle_node内部是一个链表
6. 至此,写操作成功,将当前节点的所有上层节点的共享锁释放,将当前节点的排他锁释放。
内存回收

由于写操作采用Copy On Write的机制,所以每次成功的写操作都会替换下至少一个TBtreeNode节点,如上图,圆形的节点代表替换下来的TBtreeNode。每次写操作所有的替换下来的节点都用一个TRecycleNode节点管理起来,上图中的方形节点。全局只有一个这样的TRecycleNode链表,每次写操作开始都会从线程局部分配一个TRecycleNode,随后将替换下来的TBtreeNode全部纳入其中,最后写操作结束时,将TRecycleNode挂在链表的最右端,即尾部。
由于读操作不加锁,所以被某次写操作替换下来的TBtreeNode不能马上重用,因为这个节点可能正在被别的线程读。需要一种机制来从全局的TRecycleNode链表中回收不再被任何线程访问的TRecycleNode和TBtreeNode以重用。节约内存。
目前实现采用的机制比较特殊:全局有两个链表指针,一个指向链表头,一个指向链表尾。每次读写操作开始时,都需要对链表尾部节点加上引用计数。操作结束时,减引用计数。如上图中的TRecycleNode B,回收过程保证引用计数不为0的TRecycleNode及其内部的TBtreeNode不被回收或重用。由于只有写操作才需要分配TRecycleNode和TBaseNode,所以每次写操作结束后都会试图去全局TRecycleNode回收链表中去回收节点,实际上,只有线程发现线程局部可用节点数低于某个阈值才会去全局TRecycleNode回收链表中回收。
回收的过程就是去拿一把全局的锁,拿到了就可以进行回收:从链表头部开始检查回收TRecycleNode 引用计数为0的节点,一直回收到线程局部可用节点达到某个阈值上限。
显然,当TRecycleNode引用计数为0时,代表它及其内部的TBtreeNode不会再被任何线程访问,可以安全的回收。
具体实现中,这其中有一些需要注意的实现细节,不再赘述。这种方法某种程度上会影响写性能和内存突增,因为写操作结束后,回收内存的过程是多线程并发的,大家抢一把全局锁,只有抢锁成功的线程才能进行回收。没有抢到锁的线程不能回收任何节点内存到线程局部,导致后续该线程执行写操作时就需要从系统分配内存。后续将改进这个过程。
读操作全程不加锁,不赘述。
锁实现
以上描述的锁是通过qlock实现的,原理如下:
并发写Btree原理剖析的更多相关文章
- 《java学习三》并发编程 -------线程池原理剖析
阻塞队列与非阻塞队 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到 ...
- 写给 Android 应用工程师的 Binder 原理剖析
写给 Android 应用工程师的 Binder 原理剖析 一. 前言 这篇文章我酝酿了很久,参考了很多资料,读了很多源码,却依旧不敢下笔.生怕自己理解上还有偏差,对大家造成误解,贻笑大方.又怕自己理 ...
- 黑马vue---40、结合Node手写JSONP服务器剖析JSONP原理
黑马vue---40.结合Node手写JSONP服务器剖析JSONP原理 一.总结 一句话总结: 服务端可以返回js代码给script标签,那么标签会执行它,并且可带json字符串作为参数,这样就成功 ...
- synchronized原理剖析
synchronized原理剖析 并发编程存在什么问题? 1️⃣ 可见性 可见性:是指当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的最新值. 案例演示:一个线程A根据 boolea ...
- 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】
原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...
- 【Xamarin 跨平台机制原理剖析】
原文:[Xamarin 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原 ...
- iPhone/Mac Objective-C内存管理教程和原理剖析
http://www.cocoachina.com/bbs/read.php?tid-15963.html 版权声明 此文版权归作者Vince Yuan (vince.yuan#gmail.com)所 ...
- 【Xamain 跨平台机制原理剖析】
原文:[Xamain 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原生 ...
- MapReduce/Hbase进阶提升(原理剖析、实战演练)
什么是MapReduce? MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算.概念"Map(映射)"和"Reduce(归约)",和他们 ...
随机推荐
- 【CF603E】Pastoral Oddities cdq分治+并查集
[CF603E]Pastoral Oddities 题意:有n个点,依次加入m条边权为$l_i$的无向边,每次加入后询问:当前图是否存在一个生成子图,满足所有点的度数都是奇数.如果有,输出这个生成子图 ...
- Unity3D笔记 Collect
一.输入轴 默认输入轴: Horizontal 和 Vertical被映射到w, a, s, d键和方向键 Fire1, Fire2, Fire3被分别映射到Ctrl,Option(Alt)和Comm ...
- Spark2 生存分析Survival regression
在spark.ml中,实现了加速失效时间(AFT)模型,这是一个用于检查数据的参数生存回归模型. 它描述了生存时间对数的模型,因此它通常被称为生存分析的对数线性模型. 不同于为相同目的设计的比例风险模 ...
- CentOS 6U7分区大于2TB的磁盘以及挂载大于16TB分区磁盘的解决方案
一.内容介绍1.问题描述1).问题一 CentOS 6.x 在格式化大于16TB的ext4分区时,会提示如下错误: mke2fs 1.41.12 (17-May-2010)mkfs.ext4: Siz ...
- CodeForce 832A Sasha and Sticks
A. Sasha and Sticks time limit per test2 seconds memory limit per test256 megabytes inputstandard in ...
- 《机器学习实践》程序清单3-7 plotTree函数
这个plotTree函数,比较聪明,比较简化,比较抽象,作者一定是逐步优化和简化到这个程度的.我是花了小两天时间,断断续续看明白的,还是在参考了另一篇文章以后.这里是链接http://www.cnbl ...
- NEFU 117 - 素数个数的位数 - [简单数学题]
题目链接:http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=117 Time Limit:1000ms Memory Limi ...
- Bitwise and Bit Shift Operators 位运算 取反 补码
Bitwise and Bit Shift Operators (The Java™ Tutorials > Learning the Java Language > Language B ...
- svn冲突的解决
svn文件冲突的解决 冲突后,会产生三个多余的文件. ①文件名.扩展名.mine 这是你的文件,在你更新你的工作副本之前存在于你的工作副本中--也就是说,没有冲突标志.这个文件 除了你的最新修改外没有 ...
- 网络攻防大作业——用python实现wifi破解
实验内容:不借助其他工具,用python暴力破解wifi 实验工具:python3.6+pywifi模块+密码字典 实验环境:Windows7(64bit) 实验思路: 首先搜索附近的wifi,将这些 ...