编译自:http://www.aosabook.org/en/500L/dbdb-dog-bed-database.html 作者:Taavi Burns 翻译:鸿 如有翻译问题或建议,请公众号留言
前文点击链接:DBDB: 一个简单的key/value数据库(一)
读取数据 从example.db读取key值:foo
$ python -m dbdb.tool example.db get foo
这里是运行dbdb.tool的main()方法:
# dbdb/tool.py
def main(argv):
if not (4 <= len(argv) <= 5):
usage()
return BAD_ARGS
dbname, verb, key, value = (argv[1:] + [None])[:4]
if verb not in {'get', 'set', 'delete'}:
usage()
return BAD_VERB
db = dbdb.connect(dbname) # CONNECT
try:
if verb == 'get':
sys.stdout.write(db[key]) # GET VALUE
elif verb == 'set':
db[key] = value
db.commit()
else:
del db[key]
db.commit()
except KeyError:
print("Key not found", file=sys.stderr)
return BAD_KEY
return OK
connect()函数打开一个数据库文件,并且返回一个DBDB实例:
# dbdb/__init__.py
def connect(dbname):
try:
f = open(dbname, 'r+b')
except IOError:
fd = os.open(dbname, os.O_RDWR | os.O_CREAT)
f = os.fdopen(fd, 'r+b')
return DBDB(f)
# dbdb/interface.py
class DBDB(object):
def __init__(self, f):
self._storage = Storage(f)
self._tree = BinaryTree(self._storage)
DBDB引用了一个Storage实例,self._tree也引用了此实例。DBDB实例可以通过字典查找(db [key])来获取键值。同时Python也会调用DBDB.__getitem__()。BinaryNodeRef是一个特殊的ValueRef,它知道如何对BinaryNode进行序列化和反序列化。
# dbdb/logical.py
class LogicalBase(object):
# ...
def get(self, key):
if not self._storage.locked:
self._refresh_tree_ref()
return self._get(self._follow(self._tree_ref), key)
get()会首先判断数据是否锁住了。(原子性的核心)
# dbdb/logical.py
class LogicalBase(object):
# ...
def _refresh_tree_ref(self):
self._tree_ref = self.node_ref_class(
address=self._storage.get_root_address())
_refresh_tree_ref使用磁盘上的数据来重置树的“视图”从而读取最新的数据。如果在读取数据时,数据被锁了会怎么样? 这意味着其他一些进程可能正在改变现在正在读取数据,此时读取的数据有可能不会与当前的数据内容保持一致,这就是“脏读”。 DBDB允许多进程访问数据,而不用担心阻塞,牺牲的代价则是数据稍微过时。
# dbdb/binary_tree.py
class BinaryTree(LogicalBase):
# ...
def _get(self, node, key):
while node is not None:
if key < node.key:
node = self._follow(node.left_ref)
elif node.key < key:
node = self._follow(node.right_ref)
else:
return self._follow(node.value_ref)
raise KeyError
这是标准的二叉树搜索遍历所有节点。 Nodes和NodeRefs是value objects:它们是不可变的并且内容永远不会改变。 节点由关联的key/value和左右侧的子节点创建,它们同样的也永远不会改变。 整个二叉树的内容只有在根节点修改时才会发生改变。 这意味着搜索数据时,不需要担心二叉树树的内容发生改变。一旦找到相关的值,它会被main()方法写入stdout而不添加任何额外的换行符,以精确的保留用户的数据。