BoltDB实现机制分析

Bolt是根据Howard Chu的LMDB项目开发的一个存粹的go语言版的key/value存储. 它的目标是为项目提供一个简单、高效、可靠的嵌入式数据库而不是要求一个完成的数据库服务器,例如Postgres和MySQL. 用作者的话说,Bolt只提供简单key/value存储,没有其他的特性,以后也不会有,加上Bolt原本的代码精简,质量高,很适合作为对数据库工作原理的感兴趣的同学的第一选择

Bolt的实现

主要的数据结构

Bolt的主要数据结构有DB,Bucket,Tx,Cursor,node,inode,page

DB表示数据库中的Bucket集合,Bucket表示数据库中的键值对集合,好比DB是Mysql,Bucket是Mysql中的一张表,键值对对应Mysql表中的一行数据. Cursor表示一个迭代器,该迭代器可以按顺序遍历bucket中的所有键/值对. Tx表示数据库中的一个只读或读写事务,只读事务可以用来创建Cursor,检索数据,读写事务还可以创建,删除Bucket和键. node表示一个在内存中的反序列化的page,inode是一个node中的内部节点,可以用来指向page中的元素或者还未被被添加到page中的元素。通常说的键值对就对应一个inode.

存储过程

不管是添加,删除还是读取,都需要先打开数据库,并获取一个事务,读事务中不允许进行任更改,只能检索bucket、检索值. 在读写事务结束时如果没有错误则提交事务,对应数据被更改,否则事务期间的所有的更改都被丢弃.不管是读取,删除还是写入,都需要通过Cursor来找到对应键的位置信息.如果是读取,比较对应位置的键和目标键是否相等,相等则找到并且返回,不相等则不存在返回nil.如果是删除,也比较对应位置的键和目标键是否相等,相等则删除对应数据.添加的话则在对应的位置插入对应值.

其中查找特定键的实现是用B+树。在提交事务时,如果有删除操作则检查是否需要重新平衡,如果有数据更改则写入到Dirty Page,写入时检测是否有node需要拆分.其中着重说明的是有数据更新的话并且数据长度不一致,很可能导致数据所在Page的改变(默认情况下Bolt的Page在使用%50时就会切页,大数据块也可能占据连续的几页达到100%),这一块的代码很值得一读,会使你对B+树的使用,还有数据的实际存储有更深刻的理解,也会对平常说的分表提升性能(减少了更改数据时整个数据的移动)有更进一步的认识。