事务并发控制
在数据库系统领域,并发控制机制主要有两种:锁和多版本机制。
加锁
一次性锁协议
事务开始时,即一次性申请所有的锁,之后不会再申请任何锁,如果其中某个锁不可用,则整个申请就不成功,事务就不会执行,在事务尾端,一次性释放所有的锁。一次性锁协议不会产生死锁的问题,但事务的并发度不高。
两阶段锁协议
整个事务分为两个阶段,前一个阶段为加锁,后一个阶段为解锁。在加锁阶段,事务只能加锁,也可以操作数据,但不能解锁,直到事务释放第一个锁,就进入解锁阶段,此过程中事务只能解锁,也可以操作数据,不能再加锁。
两阶段锁协议使得事务具有较高的并发度,因为解锁不必发生在事务结尾。它的不足是没有解决死锁的问题,因为它在加锁阶段没有顺序要求。如两个事务分别申请了A, B锁,接着又申请对方的锁,此时进入死锁状态。
树形协议
假设待加锁的数据项的集合满足一个偏序关系,访问数据项必须按此偏序关系的先后进行。如di->dj,则要想访问dj,必须先访问di。这种偏序关系导出一个有向无环图(DAG),因此称为树形协议。
树形协议的规则有:
- 树形协议只有独占锁;
- 事务T第一次加锁可以对任何数据项进行;
- 此后,事务T对数据项Q的加锁前提是持有Q的父亲数据项的锁;
- 对数据项的解锁可以随时进行;
- 数据项被事务T加锁并解锁之后,就不能再被事务T加锁。
树形协议优缺点:
- 优点是并发度好,因为可以较早地解锁。并且没有死锁,因为其加锁都是顺序进行的。
- 缺点是对不需要访问的数据进行不必要的加锁。
时间戳排序协议
事务在加锁时存在粒度的区别:
如数据库锁,表锁,行锁,字段锁;页锁等。不同的数据库支持的锁粒度不同,BerkeleyDB支持页锁,即对数据项所在的内存页加锁。
事务的隔离等级
基本概念
- 脏读:又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。
- 不可重复读: 在同一个事务内,两个相同的查询,对同一条数据返回了不同的结果。
- 幻读: 在同一个事务内,两个相同的查询,返回的记录数不同。
隔离等级
- READ_UNCOMMITTED: 隔离等级最低,可以读取其他事务未提交的数据,导致脏读,更无法避免不可重复读和幻读。一般不采用。
- READ_COMMITTED: 只能读取其它事务已经提交的数据,可以避免脏读,但是无法避免不可重复读和幻读。
- REPEATABLE_READ: 只能读取当前事务开始之前已经提交的事务修改的数据,可以避免不可重复读,但是无法避免幻读。
- SERIALIZABLE_READ: 隔离等级最高,可以避免幻读,但是并发度大大减小。一般也不采用。
不可重复读和幻读
不可重复读和幻读看起来结果是一样的:在同一个事务中的两次查询得到了不同的结果。
但是从控制的角度来看是有很大区别的:
- 避免不可重复读只需要对查询限定的数据加锁,防止修改即可。在数据库中即加行锁即可。
- 避免幻读还需要对查询将要涉及的所有数据枷锁,防止增加和删除。在数据库中要加表锁才行。