首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

DBDB:一个简单的key/value数据库(三)

编译自:http://www.aosabook.org/en/500L/dbdb-dog-bed-database.html

作者:Taavi Burns

翻译:鸿

如有翻译问题或建议,请公众号留言

插入和更新数据

将key值foo对应的value值bar插入到example.db中:

同样的进入main()方法:

__setitem__方法通过_assert_not_closed方法保证DB数据库是可以使用的,并且调用_tree方法更新key/value值。_tree.set()由LogicalBase提供:

set()方法首先检查数据是否上锁:

值得注意的是:

1.这儿的锁是由portalocker模块提供

2.如果数据库锁上了返回False,否则就是True

再回到_tree.set(),通过判断lock()的返回值,调用_refresh_tree_ref作为新的根节点,所以不会丢失其他进程可能正在进行的更新。这样的话,只要等待磁盘刷新树再用它将包含插入(或更新)的键/值的新树替换根节点。因为_insert()返回一个新的树,所以插入或更新二叉树不会改变任何节点,并且新树会与前一棵树共享不变的部分以节省内存和执行时间。

DBDB总是返回一个包装在一个NodeRef中的新节点,所以不用通过更新节点来指向新的子树,而是创建一个共享未改变的子树的新节点。 目前所做的只是通过移动树节点来控制磁盘数据。为了这些修改写入磁盘,我们需要对commit()进行显式调用。

LogicalBase里的self._tree_ref实际上是BinaryNodeRef(ValueRef的子类)类

_referent实际上是BinaryNode,调用的store_refs

这里是递归的调用store方法,将数据存储。回到store方法:

NodeRef的_referent确保已经递归结束,referent_to_string进行序列化:

到这里所有的节点都被写入磁盘了,回到commit()方法,commit_root_address解决所有剩下的事。

这里保证了文件句柄被成功刷新(操作系统可以将所有数据保存到稳定的存储器中)并给出根节点的地址。由于根节点地址同时拥有旧值或新值,其他进程可以从数据库中读取而不需要获得锁。外部进程可能会看到不同状态的二叉树树,但并不会混淆这两种树。所以commit具有原子性的。由于我们在写入根节点地址之前就将新数据写入磁盘并调用了fsync syscall2,因此未commit的数据是无法访问的。相反,一旦根节点地址被更新,它引用的所有数据也在磁盘上。所以,commit也具有持久性。NodeRefs如何存储数据:这是为了避免整个二叉树结构同一时间都保存在内存当中,当从磁盘读入逻辑节点时,其左右子节点的磁盘地址(及其值)也会被加载到内存中。一个NodeRef就是一个地址:

调用get()方法会不断遍历地址:

当对二叉树的更新没有commit时,它们会保存在内存中。 这些更新不会保存到磁盘中,因此更新的节点会包含具体的key和value,但是没有磁盘地址。在执行写入的过程可以看到没有commit的更新,并且在确认commit之前可以继续更新,因为NodeRef.get()会返回未commit的值(如果有的话);在通过API访问时,commit数据与未commit数据之间是没有区别的。 所有更新都是原子性的。并发更新则会被磁盘上的锁阻塞。锁是在第一次更新时获取并在commit后释放。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180427G1ZB5600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券