My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Shelve2Book  

shelve2.py 嵌入式对象数据库使用指南

shelve2.py 对象数据库是 Quixote 的子项目 Durus 对象数据库 的衍生程序, 使用 CNRI 开源许可协议发布。

shelve2.py 是一种极其轻便的嵌入式对象数据库, 可以运行在任何支持 Python 2.5 的系统上 (包括 Stackless Python 2.5)。如果用户具有 ZODB 或者 durus 等对象数据库的使用经验, 会有助于理解 shelve2.py 的使用方法和工作方式, 但是这不是必须的, 因为 shelve2.py 要简单得多。

hello world!

# from shelve2 import open
from shelve2 import lazy as open # 使用 lazy 代替 open,
                                 # 可以利用惰性 IO 避免频繁的 IO 读写

d = open('test.fs', 'c') # 第二个参数是数据库文件的打开方式,
                         # 'c' 是指以读写方式打开, 如果文件不存在则自动创建
                         # 可以使用 'r' 以只读方式打开

d['foo'] = {'a': 1, 'b': 2, 'c': 3}

#d.sync() # 强制保存
d.close()

d2 = open('test.fs')
print d2['foo']

保存自定义对象

from shelve2 import lazy as open

class MyObject:
	def __init__(self):
		self.a = None

	def print_a(self):
		print self.a

d = open('test.fs', 'c')
d['myobj'] = MyObject()
d['myobj'].a = 'hello world!'
d.close()

# 注意, 在打开数据库时, 对象定义 class MyObject 必须存在
# 否则会得到 AttributeError 的异常
d2 = open('test.fs')
d2['myobj'].print_a()

使用 Persistent 创建持久对象结点

from shelve2 import lazy as open
from shelve2 import Persistent

d = open('test.fs', 'c')
obj = d.new(Persistent)() # 创建持久对象结点,
                          # 显式指定持久对象归属于数据库 'd'

obj.a = 'hello'
obj.b = 'world'
d['obj'] = obj
d.close()

d2 = open('test.fs')
obj = d2['obj']
print '%s %s!' %(obj.a, obj.b)

在对象数据库中, Persistent 不同于一般的 Object。他们的区别在于在 Persistent 对象和普通 Object 同时作为父对象的属性时, 当父对象被展开、取出对象数据库时, 作为父对象属性的常规 Object 也会被自动展开读取到内存中; 而 Persistent 对象在父对象展开时仅以指针形式存在, 不会被展开到内存中。所以 Persistent 对象更节省内存并节约 IO, 这使得 Persistent 对象非常适合作为数据节点。

你可能注意到于 ZODB 或者 Durus 数据库不同, shelve2.py 需要显式指定持久对象的数据库归属。这正是 shelve2.py 被设计出来的重要原因之一。ZODB 和 Durus 会为每一个线程隐含指定一个互相独立的连接 (Connection), 但是对于 Stackless Python 的 tasklet 线程来说, 这或许不大方便 (Eurasia3 正是基于 Stackless Python 的), 因为 tasklet 和常规意义上的线程还是有很大的不同。所以 shelve2.py 认为有必要把连接的选择权交还给用户。

定制 Persistent 对象

from shelve2 import lazy as open
from shelve2 import Persistent

class Foo(Persistent):
	def __init__(self, a, b):
		self.a = a
		self.b = b

	def print_ab(self):
		print '%s %s!' %(self.a, self.b)

d = open('test.fs', 'c')
d['foo'] = d.new(Foo)('hello', 'world')
d.close()

d2 = open('test.fs')
d2['foo'].print_ab()

自定义 Persistent 对象需从 Persistent 类继承。

使用 BTree 创建大容量数据结点

from shelve2 import lazy as open
from shelve2 import BTree

d = open('test.fs', 'c')
tr = d.new(BTree)()
for i in xrange(1000):
	tr[i] = `i`

d['tr'] = tr
d.close()

d2 = open('test.fs')
print d2['tr']

BTree 对象可以用以保存数以千万计的对象, 同时被保存的这些对象是按照 Key 排序的, 你可以以任何方式定位、遍历这些内容。

BTree 除了支持所有类似于 dict 的方法外, 还支持各种遍历方法:

  • 正向遍历 . . iter . .()
  • 反向遍历 . . reversed . .()
  • 正向遍历返回键、值 iteritems()
  • 反向遍历返回键、值 items_backward()
  • 正向遍历返回键、值 items_from(key, closed=True)
  • 反向遍历返回键、值 items_backward_from(key, closed=True)
  • 遍历区域返回键、值 items_range(start, end, closed_start=True, closed_end=False)

BTree 对象可以以和 Persistent 对象类似的方法派生。

shelve2.py 对象数据库的根对象就是一个 BTree 对象。

聊点别的

下面我们来聊点别的。

Persistent 和 BTree

在 Zope/Plone 中, 不仅文件是 Persistent 对象, 连文件夹也是 Persistent 对象, 此时一个文件就是 Persistent 对象的一个属性。只有在文件夹过大的时候我们才会使用从 BTree 派生出来的 PloneLargeFolder。所以从本质上说, Persistent 和 BTree 都是容器对象, 他们的区别在于 Persistent 对象的内容在数据库里是连续存储, 而在内存中则变成 Hash Table, 在展开 Persistent 对象时其所有的属性也会随之一次性全部展开; 而 BTree 对象无论在数据库中还是内存中都是以平衡树的方式存储的, 读取 BTree 对象时 BTree 对象中的内容不会一次完全展开, 而是按需要分批展开。

所以在需要频繁读取容器内容时、并且容器中放置的内容并不多的情况下 Persistent 会快于 BTree。而当保存的内容很多时 BTree 要更快。但是我们往往很难知道某个容器将来到底会扩充到何种规模, 所以可以在任何时候都使用 BTree 作为容器, 在性能上这其实并无大碍。

shelve2.py 默认使用 gdbm 底层

没错, shelve2.py 底层使用的确实是已经淘汰很久的 gdbm 引擎。其实最直接的原因是 想在自己的嵌入式平台上使用 shelve2.py 数据库, 这种机器不仅慢而且没多少内存, 使用 gdbm 恰能得到最好的性能。

好吧, 说回来, 对于对象数据库而言, gdbm 足够简单、轻量级, 而且够用。而 bsddb 和 Any DB (不用提醒, 我知道 Any DB 可能会指定 gdbm) 有些不太可控, 而且对于对象数据库未必会有优势, 所以 shelve2.py 直接指定 gdbm 作为存储引擎, 就是这样。

切换底层引擎

你可以阅读 shelve2.py 源码, 通过参考 shelve2 内建的 Connection 类和 open 函数可以简单地将 shelve2.py 移植到目前大部分数据库引擎上去。包括 sqlite、MySQL、GAE、memcached 和 sina 的 memorycachedb 等。

你只需利用上述数据库引擎实现一个 dbm (dict), 包含 getitem 、 setitem 、 delitem、has key、sync、close 这六个接口即可。如果你不知道怎么编写 Connection/open, 请把 dbm 实现寄给我 wileishn(at)gmail.com 或者 Eurasia3 用户组, 我会把你提供的支持加进 shelve2 专门的引擎支持库, 并把你写进贡献者名单。

shelve2.py 的性能怎么样, 能存储多大的数据量?

shelve2.py 更接近 durus (不使用 C 扩展, 即使使用了 C 扩展也未必会比 shelve2 快, 这点需要实测一下) 数据库的性能。对于简单的二维平面数据 bsddb 和关系数据库更有优势, 对于树形数据和复杂的逻辑数据对象数据库更有优势。对此你应该有自己的判断。

在容量上, BTree 可以存储近乎无限的对象数量。

作为 Missile DB 的组成部分 shelve2.py 的目标是够用, 而 Missile DB 数据库则会提供更为强大的性能和智能。目前 Missile DB 尚在开发中, shelve2.py 因为比较独立故先期发布。

shelve2.py 目前正在实际项目中进行测试, 我们本身并不担心 shelve2.py 数据库的性能 (只有初学者才会对性能敏感), 我们只关心它的稳定性。

多线程以及多进程并发

对于并发状态下的读写控制, 需要说明的是目前 (0.0.1 alpha1 版) 仍需手工控制, 我们应该很快会加进这方面的内容。在 0.0.1 alpha2 版以后你可以忽略上面这行说明。


Sign in to add a comment
Powered by Google Project Hosting