基础
1:一些DB句柄是线程安全的,一些句柄在创建时可以提供一些flags来获得安全性
2:DB提供独占和非独占锁
3:Berkeley DB称线程安全叫Free-thread,也就是数据和对象不需要显式的加锁就能保证安全性
4:DB如果得不到锁就会被阻塞,DB提供了一些办法检测和化解死锁
线程安全的句柄
DbEvn
环境打开的时候使用DB_THREAD
Db
打开数据库的时候使用DB_THREAD或者你使用了一个线程安全的环境
Dbc
游标不是线程安全的,你必选显式的加锁
DbTxn
同游标
锁保证
1:其他线程不能读锁定的数据(独占锁)
2:其他线程不能修改数据(独占和非独占锁)
锁定资源
1:锁拥有者
在一个事务中,锁拥有者是事务句柄,在非事务中,锁拥有者是游标或者数据库句柄
2:锁
锁是一个数据结构,在DB的锁管理器中被描述
3:被锁定对象
一般情况下,被锁定的对象是数据库页,在Queue中,被锁定的对象是单个记录
你能够配置有多少锁拥有者,锁,被锁定对象
锁的类别
1:独占锁
屏蔽一切其他线程的访问
2:非独占锁
只能读取数据,可以同时有多个locker使用非独占锁锁定同一个对象
非独占锁能够防止你在读取数据的时候别人却在修改数据,事务的游标就是使用的非独占锁直到事务的提交或者放弃,可以通过快照隔离(snapshot isolation)避免使用锁
锁的生命周期
1:事务的锁直到事务提交或者放弃
2:非事务的锁直到这个操作完成;例如游标,游标释放锁直到游标移动到下一个位置或者游标关闭
阻塞
可以通过配置让这些锁不阻塞线程
减少锁竞争
1:降低你的隔离级别
2:使用快照隔离级别代替读锁
避免死锁的建议
1:一个线程任何时刻只有一个事务,一个简单的方法是使用auto commit
2:确保获得锁的顺序是一致的
3:如果你使用BTree,关闭reverse split
4:?
锁子系统的配置
1:基础
1:DbEvn::open()指定DB_INIT_LOCK
2:在环境打开之前,你可以进行一些配置,注意,这些配置会应用到所有环境
3:同样可以配置DB_CONFIG
2:具体配置
1:环境中locker数量:默认1000,DbEvn::set_memory_init()配置DB_MEM_LOCKER;另外你还可以配置DB_CONFIG的set_lk_max_lockers
2:环境中锁的数量:默认1000,DbEvn::set_memory_init()配置DB_MEM_LOCK,另外你还可以配置DB_CONFIG的set_lk_max_locks
3:环境中被锁定的对象的数量:默认1000,DbEvn::set_memory_init()配置DB_MEM_LOCKOBJECT,另外你还可以配置DB_CONFIG的set_lk_max_objects
3:死锁检测配置
1:设置允许DB检测死锁
DbEvn::set_lk_detect(),锁子系统会在线程请求锁但被阻塞后去检查锁表以判断是否产生了死锁
2:使用专一的线程检测死锁
3:设置锁超时时间
下一章是化解死锁的方式...
下下章是设置事务优先级,当死锁产生时以决定放弃那个事务等等...
事务隔离级别
1:问题
1:更新丢失(Lost update):两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。
2:脏读(Dirty Reads):一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。
3:不可重复读(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如,在两次读取的中途,有另外一个事务对该行数据进行了修改,并提交。
4:两次更新问题(Second lost updates problem):无法重复读取的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。
5:幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。
2:3种级别
1:Read Uncommitted:允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。
2:Read Committed:允许不可重复读取,但不允许脏读取。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
3:Serializable:提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
DB默认隔离级别是level 3
接下来讲的是如何配置使用这几种隔离级别,死锁相关的,采用非阻塞的方式使用锁,这些都和我们程序相违背,不看了
Reverse BTree Splits
默认情况下,BTree会自动回收空页,页面会变得更少,但因为锁子系统是以页面为单位来进行锁定的,更少的页面以为着更低的并发性,所以可以关闭自动回收
Db::set_flags(),DB_REVSPLITOFF