ISOLATION LEVELS
SQL 规定了以下隔离级别:Read Uncommited: 读已提交的。
Read Commited: 仅读已提交的数据。显然,可以防止脏读。但是它不能保证此数据在本事务完成之前不被其他事务修改,所以有可能发生不可重复读现象。
Read Repeatable: 给本事务读的所有数据加上共享锁,这样可以防止不可重复读,因为其他事务无法在本事务完成之前修改这些数据。但是幻觉读仍有可能出现,因为另一个事务可以向该表中插入某些数据。
Read Serializable: 仅读已串行化的数据,这表明,幻觉读是不可能出现的了。
数据库调度的特征:
- 基于可恢复性的调度,可恢复的调度(recoverable schedule):是指已提交的事务不应该发生回滚的调度,它可以分为以下几类:
- 层联回滚(Cascading rollback)调度:未提交的事务从未提交的(失败)事务中读取了错误的数据(Dirty Read ),必须回滚。
- 无层联回滚的:事务只读取已提交事务写的数据(Read Commited)。
- 严格调度(Strict Schedule):在写数据项的最后一个事务提交之后事务才能开始读/写该数据项。
- 基于可串行化的调度(略);
- 加锁协议(Locking Protocol) :
- 可以加Read_Lock和Write_Lock(也就是以前讲的共享锁和互斥锁)。
- 可以加Read_Lock和Write_Lock(也就是以前讲的共享锁和互斥锁)。
- 为了保证事务的可串行性,事务必须遵循以下规则- 称作Two Phase Locking Scheme(2PL),在这个协议里,食物可以分为两个阶段:
- Growing phase: 只加锁,不释放锁(但是可以进行锁升级);
- Shrinking phase: 只释放锁,不加锁(但是可以进行锁降级);
所谓的锁升级(降级)是指对同一个对象的读锁变为写锁(或反过来)。 若事务满足了这两个特征,便是
可串行化的(Serializable):
原因不难分析,简单的说,因为在一个事务在执行某个操作前必须得到其读/写对象的锁(在此之前不能释放锁),若不能得到就只有等待其他事务释放该锁(而其他事务一旦开始释放锁就不能再申请锁),故对于冲突的操作在两个不同的事务之间只能按一定顺序执行(在优先图中表现为没有环)-对于非冲突的操作则可以按任意次序执行。
这里还可以看到,满足2PL的调度能大量地减少并行度。并且在2PL中申请锁与释放锁这两个过程完全独立,与具体封锁的数据对象无关,所以2PL只能与串行执行集的一个子集等价,换句话说, 2PL对于可串行化来说太严格了( 2PL is 2 strict 4 serializablity)。
但是满足2PL的事务可能需要 层联回滚,这是因为事务可以读取未提交的数据 (因为另一个事务在提交前释放了互斥锁)。于是人们提出了 严格2PL( Strict 2PL, S2PL)协议:事务仅在提交或异常中止后才释放其互斥锁-暗含着其他事务仅能在T提交后读/写T所写的数据对象 - S2PL保证了严格调度( 不仅仅是免层联回滚,因为2PL协议已暗含了读/写互斥)。
S2PL不能防止死锁,要防止死锁,可以采用 保守的2PL( C2PL),即一个事务在开始之间就事先声明其读、写集。也可以采用时间戳( TimeStamp)的方法。
CCM
(
Concurrent control mechanism
) = well formed + 2PL
这里还可以看到,满足2PL的调度能大量地减少并行度。并且在2PL中申请锁与释放锁这两个过程完全独立,与具体封锁的数据对象无关,所以2PL只能与串行执行集的一个子集等价,换句话说, 2PL对于可串行化来说太严格了( 2PL is 2 strict 4 serializablity)。
但是满足2PL的事务可能需要 层联回滚,这是因为事务可以读取未提交的数据 (因为另一个事务在提交前释放了互斥锁)。于是人们提出了 严格2PL( Strict 2PL, S2PL)协议:事务仅在提交或异常中止后才释放其互斥锁-暗含着其他事务仅能在T提交后读/写T所写的数据对象 - S2PL保证了严格调度( 不仅仅是免层联回滚,因为2PL协议已暗含了读/写互斥)。
S2PL不能防止死锁,要防止死锁,可以采用 保守的2PL( C2PL),即一个事务在开始之间就事先声明其读、写集。也可以采用时间戳( TimeStamp)的方法。
防止幻觉读可以使用的锁有断言锁和索引锁。
- Predicate lock(断言锁)
- Index Lock(索引锁)