并发写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(归约)",和他们 ...
随机推荐
- 基于pandas python的美团某商家的评论销售(数据分析)
数据初步的分析 本文是该系列的第一篇 数据清洗 数据初步的统计 第二篇 数据可视化 第三篇 数据中的评论数据用于自然语言处理 from pyecharts import Bar,Pie import ...
- C3P0连接池配置(C3P0Utils.java)
配置文件 c3p0-config.xml <?xml version="1.0" encoding="UTF-8"?> <c3p0-confi ...
- FTP的两种主动模式和被动模式
参考:https://blog.csdn.net/xqhrs232/article/details/54633006 https://blog.csdn.net/yuanhangq220/articl ...
- Centos7.2安装ruby用于爬虫脚本
1,系统版本查看 2,安装依赖包 yum -y install ruby-devel yum -y install mysql-devel yum -y install gcc-c++ gcc r ...
- Scikit-learn技巧(拓展)总结
Scikit-learn技巧(拓展)总结 本文转载自:http://www.jianshu.com/p/516f009c0875 最近看了<Python数据挖掘入门与实战>,网上有说翻译地 ...
- HDFS Users Guide
Purpose This document is a starting point for users working with Hadoop Distributed File System (HDF ...
- PL/SQL常用语法及举例
PLSQL语句 DECLARE 声明部分 BEGIN 程序编写,SQL语句 EXECPTION 处理异常 END; / 声明部分(DECLARE) SQL> set serveroutput o ...
- 使用schemasync同步数据库表结构
安装方式 wget http://www.schemasync.org/downloads/SchemaSync-0.9.4.tar.gz tar -xf SchemaSync-0.9.4.tar.g ...
- Joint Stacks---hdu5818(栈模拟)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5818 有3个操作pop,push,merge A B; 引入一个新的栈C,每次合并的时候就把A和B合 ...
- KVM--安装及初步使用
KVM是Kernel-based Virtual Machine的简称,是一个开源的虚拟化模块,今天我将在CentOS7的操作系统上安装KVM,以下是我的安装步骤. 一.环境信息 系统: CentOS ...