Berkeley DB Replication, 以及 Python 开发, 以及 twistd 注意事项

BDB 从 4.0 开始,内建了 Replication 支持。它是在 "DB 环境" 里增加了若干日志文件来保存 Replication 所需要的信息,实际上 db 文件的格式没有为此做特别的变化;另外,它除了提供了隐藏同步的细节的 API 外,它还提供了在几个节点之间选举出新的 Master 的 API,这些 API 统一被称为 Base replication API。可以说 BDB 提供的是一个 HA 解决方案,完全超越了常见的 Master-Slave 模式。

在 BDB 最初的设想里,同步可能是在一个复杂、异构的网络环境(可能不仅仅是 IP 网络,或者需要用到 UDP 传输)里进行的,因此它最开始需要用户自己来编写网络传输的部分 + Base replication API 做开发。从 4.5 版本开始,它提供了一套基于 TCP 连接做同步的更高级的 API,"Replication Manager",开发者几乎不需要做任何工作,就可以轻松构建一个复制集群。

传统的 Master-Slave 的缺点是如果 Master 一旦垮掉,则在其恢复之前整套系统是不可写入的。但在 BDB HA 环境里,Master 垮掉后,剩余的 Slave 将选举出一个新的 Master,以后向新的 Master 写入即可,老的 Master 恢复后可能会作为一个 Slave 节点加入,或者引发一次新的 Master 选举...

注意当前的实现里面,Slave 节点是不可写的(它要是能自动将写请求转发给 Master 节点多好!)。写入者如何将写请求发送到新的 Master 上完全依赖开发者的实现。

如果要实现一个纯粹的 Master-Slave 环境——这样丧失了 HA 特性,但前端实现却很简单,读节点和写节点的网络地址都是固定的——就必须将 Slave 节点的 priority 统统设置为 0

有人在论坛上问过一个很好的问题:slave 节点里是否还需要"lock_detect"或"checkpoint"这样的操作?

官方答复是:checkpoint 就不需要了,但只要 slave 有请求,哪怕只是读操作,就需要做死锁检查

启动 REP 环境还有一点要注意的,尤其是在 slave 节点上,因为 rep_start 是启动线程后在线程里面开始从 master 同步的。如果在 rep_start 函数后就直接 db->open,有可能同步线程里面也在 db->open 而导致死锁。一个建议是等 DB_EVENT_REP_MASTER(master上) 或 DB_EVENT_REP_STARTUPDONE(slave上) 事件后再去 db->open,不过这样做仍然有危险:假如 Master 掉了,启动 Slave 是永远也得不到 startupdone 事件的。我看有的例子是除了等 startupdone 外还设置了一个等待超时时间;或者在这里加入死锁处理循环也是可行的。

对于 replication env 来说,还有一个限制就是 db 文件必须直接放在 home_dir 或 data_dir 目录下,而不能是其子目录中...

如果环境中的 db 文件很多的话,那缺省的 Mutex 数目就可能不够用了。可能是我还不熟悉 BDB,要么就实在是它的 API 报错机制不友好,总之我的应用足足折磨了我一个多星期,才无意中发现了需要调整 mutex_set_max 才能正常工作。教训是如果 BDB 节点工作不正常,首先就要用 db_stat 看看它的各项资源使用情况。

开发 BDB REP 应用,最好用 4.7.25 或更新的版本。在我的测试里面,至少其 Replication Manager 比起 4.6 要可靠一些。

bdb 的 python 绑定现在(pybsddb 4.7.0)只有 Replication Manager 的支持。从 svn trunk 来看,4.7.1 似乎将增加 Base replication API 的绑定。

我的 db 服务是基于 twisted 框架的,首先得到一个 dbenv,然后用它做参数去实例化工厂类。但用 twistd 启动时发现一个问题:当用 -noy 参数启动,一切都正常;但如果想放到后台用 -oy 参数的话,同步就不再工作了。

google 之发现在 twistd 里调用外部线程(比如 rep_start 这样 C 扩展里创建线程的,或直接调用 thread.start_new_thread 启动的)都必须在 reactor 运行后进行,切记切记

总结:bdb 还是挺博大精深的,我强烈预感未来会有 BDB Programmer or BDB DBA 的专门职业出现。至少现在我好像就很缺一个 BDB Programmer... 呜呜呜

Topic: 技术

评论

we start using bdb here just a year ago or so, and opened a project name memcachedb, which using bdb for backgrand persist storage .

yeah. 一直知道 memcachedb,但直到这次要写 bdb 应用才去看了看源代码,发现它也是用 Replication Manager API

hi,我用bdb正在写数据仓库,解决超大容量数据的问题

目前在性能优化和调试,

谢谢,我怀疑大量文件时候出错也是这个 mutex_set_max 设置问题,回去看看

我在用berkeley db 开发一个小型的数据库存储系统,遇到的一个很大的问题就是环境的移除与重建!首先我想确认,同一台pc机,一个进程创建了一个环境并写库,然后(也可能是同时)另一个进程来读这些数据库中的信息,那第二个线程应该自己创建一个环境呢?还是加入原来的环境中去?另外如果实际情况是:我先用一个进程创建环境在此下写库,然后移除环境(不知我这样做是否正确?)此进程结束,然后另一个进程再起动同时创建环境来读数据库中的信息。另外在多线程环境中,我在打开时使用了 DB_INIT_CDB 这个标志,然后用带 DB_WRITECURSOR 标志的游标来完成写,这里存在这样一个问题就是:由于一直使带此标志(DB_WRITECURSOR)游标处于打开状态可能会引读操作的阻塞从而影响访问效率,但是如果游标在完成一次put就要打开(cursor)与关闭(close)一次,则又会将时间浪费在游标的创建与关闭上,那么到底怎么做是正确的,有没有更好的办法?

环境创建了就别移除了

并发访问必须共用环境

您好,mutex_set_max 这个函数在环境里找不到。报错如下error: ‘DB_ENV’ has no member named ‘mutex_set_max’。这是怎么回事?

mutex_set_max 不是函数,是一个配置

你可以用 DB_CONFIG 文件来配置,当初始化 env 的时候,如果 env home 下有这个配置文件,它会根据这个配置文件来设置某些参数的

我已经解决了,mutex_set_max是环境里的一个函数,可以设置mutex的数目,如果db文件很多会报如下错误:unable to allocate memory for mutex; resize mutex region.

我在使用berkeleydb时,还遇到一个问题:DB->cursor: method not permitted before handle's open method。不知道是什么原因。

看这个意思是你没有 open ???