bbolt简介
什么是bbolt
一个可嵌入程序的,使用golang编写的key value数据库。项目地址:github.com/etcd-io/bbo…
为什么学习bbolt
- bbolt是云原生重要基础设施,分布式配置数据库etcd内部存储组件。学习bbolt可以作为学习etcd的支线任务。而etcd又可作为敲开分布式存储的一扇门
- 通过学习bbolt,充分理解数据库的ACID特性。即原子性,一致性,隔离,持久化
- bbolt项目代码约5000行,简单无依赖,便于学习
ACID实现原理
b+树
首先我们需要一棵b+树。b+树用语言简要描述如下,以 若干字节 为1个节点。每个节点指向若干其他节点。指向信息内包含了指向节点的地址和指向节点key的最小值。key值统一从小到大排列。没有指向其他节点的节点,成为叶子结点,且含有value值。
事务的隔离(isolation)
bbolt可以同时开启多个读事务,1个写事务。在写事务提交之前开启的读事务,对写入的数据不可感知。相当于关系数据库的可重复读。这种隔离性是如何实现的了?
- 数据的读取操作。可以将b+树的每个数据节点,视为磁盘文件的一段数据。如果需要进行读取,只需要知道b+树的根节点位置,然后顺序向下查找即可。整个操作纯文件io操作(bbolt使用了内存映射机制,以简化文件读取)。
- 数据的写操作。写操作时机发生在读取完成后,定位到实际需要插入的叶子结点的位置。此时将叶子节点,反序列化到内存中,并进行添加修改。
- 在写事务提交时,申请未被使用的文件空间,并实际写入。因为子节点的地址信息发生变化,很显然需要更新父节点信息。父节点也需要申请空闲的磁盘空间(未被现有节点占用的磁盘空间),并更新写入。由此一路更新到根节点。
- 我们可以看到整个更新操作,实际并未触动到原来的b+树,只是将整个查找路径上的节点,多写了一份。逻辑很清晰,并由此来保证事务的隔离。
事务的原子性(atomicity)
- 在查找路径上的节点整体都被额外重写了一份之后(fsync),就到了保证原子性的关键一步了,更新数据库的meta信息。meta信息将指向 b+树的根节点。用于将整个写操作实际整体生效
- meta信息存储了根节点的位置信息。meta信息写入之后,fsync将信息刷新到磁盘,可以视为整个操作完成,可成功返回了。
- 那完成了上述操作就可以视为写操作整体提交了么?是的。可以逐步推演下
| 宕机时机 | 影响评估 |
|---|---|
| b+树节点写入时崩溃 | meta根节点信息没有变更,程序未返回,事务状态未知,幂等重试 |
| meta信息写入时崩溃 | meta根节点信息可能写盘成功,可能未成功,事务状态未知,meta写入磁盘前已经计算了校验和,若meta数据读出后发现校验和对不上,则写入失败,事务实际失败 |
对比了mysql的原子性保障,可以概括为,将事务操作相关信息计算校验和,统一写盘。通过校验和判定事务整体是成功。
总结
- 为什么没有图,呃,主要是懒,画图太麻烦
- 为什么没有贴源码,呃,我觉得贴源码。。。是概括能力太弱了,看起来太费劲
- 本文的定位和价值是希望简明扼要的介绍了bbolt的ACID(迷人的)实现机制。希望有编码背景的同学能看懂
- 在读懂了本文之后,相信如果有兴趣读bbolt源码,能事半功倍。如果看bbolt源码,重点留意一下 page和node。可以简单理解为如果有node对象,一定是发生了写操作(应该是这样)
- bbolt的源码阅读我也没有面面俱到,主要是在搞明白它的acid实现机制后,就没有其他明确的疑问了