项目地址:
中文翻译
https://github.com/HT524/500LineorLess_CN
英文版:
http://aosabook.org/en/500L/dbdb-dog-bed-database.html
github代码
1 the process of load file into binary tree
使用命令
python -m dbdb.tool DBNAME set KEY VALUE
首先看如何加载数据库
在interface.py中有DBDB这个类
在init.py中设置了这个方法connect
db = dbdb.connect(dbname)
fd = os.open(dbname, os.O_RDWR | os.O_CREAT),进行创建成功
之后返回DBDB(f)
调用DBDB的init方法,有两个成员变量_storage 和_tree,分别用来存储和建树
self._storage = Storage(f)
self._tree = BinaryTree(self._storage)
Storage
self._f = f
self.locked = False
self._ensure_superblock() 设置最小为4096个字节存储空间
存储时可以发现root_address是设置在文件的最开始处的
def get_root_address(self):
self._seek_superblock()
root_address = self._read_integer()
return root_address
BinaryTree的初始化:
首先是LogicalBase(self,storage),成员变量为_storage , 和_tree_ref(类型为BinaryNodeRef类型)
self._storage = storage
self._refresh_tree_ref()
_refresh_tree_ref()中,设置_tree_ref指向最新的root地址
self._tree_ref = self.node_ref_class( address=self._storage.get_root_address())
之后是BinaryTree的初始化,没有init函数
到现在为止,系统初始化完毕
===================================================================
如何set一个值
python -m dbdb.tool DBNAME set KEY VALUE
(1)db[key] = value
db.commit()
(2)DBDB类中的_tree成员变量(BinaryTree类型,基类为LogicalBase)
def __setitem__(self, key, value):
self._assert_not_closed()
return self._tree.set(key, value)
(3)基类LogicalBase的set方法
def set(self, key, value):
if self._storage.lock():
self._refresh_tree_ref()
self._tree_ref = self._insert(
self._follow(self._tree_ref), key, self.value_ref_class(value))
基类的_follow(self,ref)方法返回ref指向的storage
BinaryNodeRef的get方法(BinaryTree._storage)
实际上是调用基类ValueRef的get方法
if self._referent is None and self._address:
self._referent = self.string_to_referent(storage.read(self._address))
return self._referent
第一次的话,referent返回的是None
所以基类LogicalBase的set方法中self._tree_ref = self._insert(None, KEY, ValueRef(VALUE))
调用子类BinaryTree的_insert方法,执行
new_node = BinaryNode(
self.node_ref_class(), key, value_ref, self.node_ref_class(), 1)
返回 self.node_ref_class(referent=new_node),返回类型为BinaryNodeRef类型
到此,insert过程结束
==================================================
再看commit函数
首先基类的commit函数
self._tree_ref.store(self._storage)
self._storage.commit_root_address(self._tree_ref.address)
第一句调用BinaryNodeRef.store方法,即ValueRef.store方法,
def store(self, storage):
if self._referent is not None and not self._address:
self.prepare_to_store(storage)
self._address = storage.write(self.referent_to_string(self._referent))
referent为insert新建的节点,节点类型为BinaryNodeRef,新创建的节点包含两个空的BinaryNodeRef,和一个ValueRef,但是没有address,因此执行self.prepare_to_store(storage)
python代码作为测试
from __future__ import print_function
import sys
class BaseA(object):
def Afunc(self):
print("Base");
def fun(self):
self.Afunc();
class DerivedA(BaseA):
def Afunc(self):
print("Derived");
if __name__ == '__main__':
d = DerivedA();
d = BaseA();
d.fun();
因此,调用BinaryNodeRef的prepare_to_store,有所指,因此调用self._referent.store_refs(storage),因此调用BinaryNode的store_refs(storage),因此调用self.value_ref.store(storage)
self.left_ref.store(storage)
self.right_ref.store(storage)
每个store函数的调用过程如下:
value_ref类型为valueRef,prepare_to_store = pass #因此忽略了。(代码中有写)
直接存储self._address = storage.write(self.referent_to_string(self._referent)),可以直接存储value,并且获得address。存储的时候,存放的是value。存储的时候,是把superBlock空间空余出来,用来存放root_address。
之后是left_ref和right_ref调用store方法,referent = None,因此忽略了。
首先基类的commit函数,之后是commit_root_address函数,把root地址写在最开始的位置。
==================================================
现在再来执行一遍
set一个值
python -m dbdb.tool DBNAME set KEY VALUE
(2)DBDB类中的_tree成员变量(BinaryTree类型,基类为LogicalBase)
def __setitem__(self, key, value):
self._assert_not_closed()
return self._tree.set(key, value)
(3)基类LogicalBase的set方法
def set(self, key, value):
if self._storage.lock():
self._refresh_tree_ref()
self._tree_ref = self._insert(
self._follow(self._tree_ref), key, self.value_ref_class(value))
基类的_follow(self,ref)方法返回ref指向的storage
BinaryNodeRef的get方法(BinaryTree._storage)
实际上是调用基类ValueRef的get方法
if self._referent is None and self._address:
self._referent = self.string_to_referent(storage.read(self._address))
return self._referent
这时,_referent与_address都不是空了。因此可以直接返回_referent,其类型为BinaryNodeRef类型。
调用子类BinaryTree的_insert方法,执行BinaryTree的下面这个方法
def _insert(self, node, key, value_ref):
if node is None:
new_node = BinaryNode(
self.node_ref_class(), key, value_ref, self.node_ref_class(), 1)
#comment 因为是一棵新树,所以它的左右分支两个self.node_ref_class()初始化时没有参数
elif key < node.key:
new_node = BinaryNode.from_node(
node,
left_ref=self._insert(
self._follow(node.left_ref), key, value_ref))
elif node.key < key:
new_node = BinaryNode.from_node(
node,
right_ref=self._insert(
self._follow(node.right_ref), key, value_ref))
else:
new_node = BinaryNode.from_node(node, value_ref=value_ref)
return self.node_ref_class(referent=new_node)
执行的是递归过程。返回的是一个BinaryNodeRef对象,指向的是BinaryNode对象,BinaryNode包含一个ValueRef以及两个BinaryNodeRef对象。
插入过程结束以后,BinaryTree中的_tree_ref更新为node_ref_class,指向新的referent
(由于创建好了以后就是不可变的,因此会浪费很多内存空间,当然也会节省一部分内存,对于某些完全没有改变的节点来说)只要节点有一点改变,都会重新设置一个节点。
===============================================
总结
每次更改数据库,并且需要commit操作,才会真正写入磁盘。从而实现——————用不可变数据结构实现一个表面上可变的对象(implement an ostensibly mutable object with an immutable data structure)。主要的类:
physical:storage 负责存储,写入
binaryTree:BinaryNode, BinaryNodeRef, BinaryTree
logical:ValueRef, LogicalBase