数据库ACID简要实现

293 阅读4分钟

bbolt简介

什么是bbolt

一个可嵌入程序的,使用golang编写的key value数据库。项目地址:github.com/etcd-io/bbo…

为什么学习bbolt

  1. bbolt是云原生重要基础设施,分布式配置数据库etcd内部存储组件。学习bbolt可以作为学习etcd的支线任务。而etcd又可作为敲开分布式存储的一扇门
  2. 通过学习bbolt,充分理解数据库的ACID特性。即原子性,一致性,隔离,持久化
  3. bbolt项目代码约5000行,简单无依赖,便于学习

ACID实现原理

b+树

首先我们需要一棵b+树。b+树用语言简要描述如下,以 若干字节 为1个节点。每个节点指向若干其他节点。指向信息内包含了指向节点的地址和指向节点key的最小值。key值统一从小到大排列。没有指向其他节点的节点,成为叶子结点,且含有value值。

事务的隔离(isolation)

bbolt可以同时开启多个读事务,1个写事务。在写事务提交之前开启的读事务,对写入的数据不可感知。相当于关系数据库的可重复读。这种隔离性是如何实现的了?

  1. 数据的读取操作。可以将b+树的每个数据节点,视为磁盘文件的一段数据。如果需要进行读取,只需要知道b+树的根节点位置,然后顺序向下查找即可。整个操作纯文件io操作(bbolt使用了内存映射机制,以简化文件读取)。
  2. 数据的写操作。写操作时机发生在读取完成后,定位到实际需要插入的叶子结点的位置。此时将叶子节点,反序列化到内存中,并进行添加修改。
  3. 在写事务提交时,申请未被使用的文件空间,并实际写入。因为子节点的地址信息发生变化,很显然需要更新父节点信息。父节点也需要申请空闲的磁盘空间(未被现有节点占用的磁盘空间),并更新写入。由此一路更新到根节点。
  4. 我们可以看到整个更新操作,实际并未触动到原来的b+树,只是将整个查找路径上的节点,多写了一份。逻辑很清晰,并由此来保证事务的隔离。

事务的原子性(atomicity)

  1. 在查找路径上的节点整体都被额外重写了一份之后(fsync),就到了保证原子性的关键一步了,更新数据库的meta信息。meta信息将指向 b+树的根节点。用于将整个写操作实际整体生效
  2. meta信息存储了根节点的位置信息。meta信息写入之后,fsync将信息刷新到磁盘,可以视为整个操作完成,可成功返回了。
  3. 那完成了上述操作就可以视为写操作整体提交了么?是的。可以逐步推演下
宕机时机影响评估
b+树节点写入时崩溃meta根节点信息没有变更,程序未返回,事务状态未知,幂等重试
meta信息写入时崩溃meta根节点信息可能写盘成功,可能未成功,事务状态未知,meta写入磁盘前已经计算了校验和,若meta数据读出后发现校验和对不上,则写入失败,事务实际失败

对比了mysql的原子性保障,可以概括为,将事务操作相关信息计算校验和,统一写盘。通过校验和判定事务整体是成功。

总结

  1. 为什么没有图,呃,主要是懒,画图太麻烦
  2. 为什么没有贴源码,呃,我觉得贴源码。。。是概括能力太弱了,看起来太费劲
  3. 本文的定位和价值是希望简明扼要的介绍了bbolt的ACID(迷人的)实现机制。希望有编码背景的同学能看懂
  4. 在读懂了本文之后,相信如果有兴趣读bbolt源码,能事半功倍。如果看bbolt源码,重点留意一下 page和node。可以简单理解为如果有node对象,一定是发生了写操作(应该是这样)
  5. bbolt的源码阅读我也没有面面俱到,主要是在搞明白它的acid实现机制后,就没有其他明确的疑问了

原文地址:mp.weixin.qq.com/s/xf3vppwIb…