在完全没有必要用上恢复机制和事件的相关语义时,通常我们会用上可并发存储的数据库.对于这类的程序,BDB提供了释放死锁(deadlock- free),数读/单写(multiple-reader/single-writer)的接口.这意味着在同一时刻,可以有多个读者访问数据,但只能有一个来修改数据.这种机制对于应用程序来说是不可见的,BDB提供了必要的锁及阻塞机制的保证这种特性.
要创建BDB并发数据存储应用程序,你需要用DB_ENV->open来初始化一个环境,并在该函数中指定DB_INIT_CDB及 DB_INIT_MPOOL的标志,若在此时同时指定了DB_INIT_LOCK,DB_INIT_TXN,DB_RECOVER之类的子系统或恢复配置的标志将产生一个错误.所有相关的数据库必须在环境中打开,并同时将该环境句柄作为一个参数来指定.
BDB通过执行相应的锁使释放死锁,数读/单写的机制对应用程序透明.当对一个并发数据存储程序进行写操作时,BDB一个基本的底层锁机制会起到很好的作用.
BDB并发数据存储并不需要通过对整个数据库加锁的死锁探测器来避免死锁,而是确保了在同一时间只允许某一线程在拥有读锁的同时可以获得写操作的权利. 所有打开的BDB游标都拥有一个读锁,以保证数据库不被它们修改;同样,所有通过DB->get操作在遍历数据库的时候暂时获得及释放一个读锁.由于读锁不会相互影响,多线程里的多个游标可以同时打开,而多个DB->get拥有同时对数据库进行操作的权利.
为实现单线程读的规则,在某一个时间内允许一个线程的读锁升级为写锁,但是,BDB必须防止多个游标同时进行写操作.BDB并发数据存储与普通的操作唯一不同的地方是:在用DB->cursor创建一个游标时指定DB_WRITECURSOR标志.该标志使新建的游标成为一个写游标,也就是一个既可读又可写的游标.只有这样创建的游标才允许对数据库进行写操作,而同一时间只允许存在一个这样的游标.
当一个写游标打开时,所有创建第二个写游标或普通写的操作将引起阻塞,直到第一个写游标关闭.而读游标却在写游标存在时正常的创建与使用.但是,任何方式的写操作,包括写游标以及直接用DB->put/DB->del的方法,将被阻塞到所有的读游标被关闭之后.防止读到由于同时读写可能造成的数据库不一致状态,以及多读/单写,就是这样被实现的.
有了这些机制,BDB可以保证无死锁的并发数据库的使用,所以多线程可以无须自己保持同步及无死锁探测器的情况下自由读写数据库.由于BDB没有能力知道哪个游标属于哪个线程,所以需要留神以保证程序不会由于自己不无意间阻塞而被挂起或无法执行,下面是常常出现的错误:
1. 在调用DB->put/DB->del时,有游标正打开.
2. 试图在同一线程中存在被打开的写游标时打开另一写游标.
注意:当线程中存在写游标时,在另一线程中打开写游标或调用DB->put/DB->del的操作是正确的,唯一的问题是,当在单独的线程中做这些事情时,该线程将被阻塞并且无法释放阻塞它的锁.
3. 长期打开一个写游标.
4. 不测试BDB的返回值.(正确的作法:当某游标操作返回错误码时,该游标应该立即关闭)
5. 默认情况下,BDB的并发数据存储的锁是以单个库为基础的.基于这个原因:当通过不同线程/进程的游标以不同的顺序对环境中不同的数据库进行操作时,或在访问其他数据库时使某数据库的游标打开,可能导致程序挂起.当必须进行这种操作时,BDB应该用基于整个环境的锁,(在DB_ENV-> set_flags时设置DB_CDB_ALLDB标志或DB_CONFIG文件).
要创建BDB并发数据存储应用程序,你需要用DB_ENV->open来初始化一个环境,并在该函数中指定DB_INIT_CDB及 DB_INIT_MPOOL的标志,若在此时同时指定了DB_INIT_LOCK,DB_INIT_TXN,DB_RECOVER之类的子系统或恢复配置的标志将产生一个错误.所有相关的数据库必须在环境中打开,并同时将该环境句柄作为一个参数来指定.
BDB通过执行相应的锁使释放死锁,数读/单写的机制对应用程序透明.当对一个并发数据存储程序进行写操作时,BDB一个基本的底层锁机制会起到很好的作用.
BDB并发数据存储并不需要通过对整个数据库加锁的死锁探测器来避免死锁,而是确保了在同一时间只允许某一线程在拥有读锁的同时可以获得写操作的权利. 所有打开的BDB游标都拥有一个读锁,以保证数据库不被它们修改;同样,所有通过DB->get操作在遍历数据库的时候暂时获得及释放一个读锁.由于读锁不会相互影响,多线程里的多个游标可以同时打开,而多个DB->get拥有同时对数据库进行操作的权利.
为实现单线程读的规则,在某一个时间内允许一个线程的读锁升级为写锁,但是,BDB必须防止多个游标同时进行写操作.BDB并发数据存储与普通的操作唯一不同的地方是:在用DB->cursor创建一个游标时指定DB_WRITECURSOR标志.该标志使新建的游标成为一个写游标,也就是一个既可读又可写的游标.只有这样创建的游标才允许对数据库进行写操作,而同一时间只允许存在一个这样的游标.
当一个写游标打开时,所有创建第二个写游标或普通写的操作将引起阻塞,直到第一个写游标关闭.而读游标却在写游标存在时正常的创建与使用.但是,任何方式的写操作,包括写游标以及直接用DB->put/DB->del的方法,将被阻塞到所有的读游标被关闭之后.防止读到由于同时读写可能造成的数据库不一致状态,以及多读/单写,就是这样被实现的.
有了这些机制,BDB可以保证无死锁的并发数据库的使用,所以多线程可以无须自己保持同步及无死锁探测器的情况下自由读写数据库.由于BDB没有能力知道哪个游标属于哪个线程,所以需要留神以保证程序不会由于自己不无意间阻塞而被挂起或无法执行,下面是常常出现的错误:
1. 在调用DB->put/DB->del时,有游标正打开.
2. 试图在同一线程中存在被打开的写游标时打开另一写游标.
注意:当线程中存在写游标时,在另一线程中打开写游标或调用DB->put/DB->del的操作是正确的,唯一的问题是,当在单独的线程中做这些事情时,该线程将被阻塞并且无法释放阻塞它的锁.
3. 长期打开一个写游标.
4. 不测试BDB的返回值.(正确的作法:当某游标操作返回错误码时,该游标应该立即关闭)
5. 默认情况下,BDB的并发数据存储的锁是以单个库为基础的.基于这个原因:当通过不同线程/进程的游标以不同的顺序对环境中不同的数据库进行操作时,或在访问其他数据库时使某数据库的游标打开,可能导致程序挂起.当必须进行这种操作时,BDB应该用基于整个环境的锁,(在DB_ENV-> set_flags时设置DB_CDB_ALLDB标志或DB_CONFIG文件).