事务隔离级别最高级是可串行化。一直对这个词感觉很费解,为什么不叫串行化?
在研究事务调度的时候,定义了事务的正确调度和不正确调度。不管事务的并发度,串行执行两个事务,这样的调度执行结果是可接受的,符合事务原子性的定义。串行事务由于不同的串行事务顺序可能会有不同的最终结果,都是可以接受的结果。
为了提高并发度,就要允许多个事务并发执行,不同的调度可能会产生不同的结果,如果不加约束,而其中有的结果是不正确的——与事务串行执行的所有可能调度顺序结果都不同。这是不可接受的,不能为了提高并发度带来“不正确的”结果。正确与否的标准就是是否存在一种串行执行结果与之相同。称为可串行化——可以通过一种串行化执行事务得到该结果。
一阶段锁
导致不可串行化的原因就是因为不同事务中读写了相同的记录,如果对所有需要修改的数据加锁是一种解决办法,一阶段锁即事务一次性请求所有锁,请求所有锁成功则事务执行并提交,否则事务失败。这种方法并发程度还不够高,基本不考虑。
两阶段锁
事务中的所有锁可以逐个请求,但是一旦释放某个锁之后就不可以在请求新的锁,只能释放所有锁。这么设计的好处是可以保证,并发事务不管如何调度 ,结果一定是可串行化的。但是无法排除死锁可能。
===================
两阶段锁是一定要保证的,但是锁的级别还有区分。
一级封锁,只有xlock,在事务结束时才可以释放。结合两阶段加锁的协议,两个事务并发都提交后,可以保证“可串行化”。但是如果事务A读取事务B修改后的数据,然后事务B回滚。这时虽然数据库的数据是可串行化的,但是A是脏读。另外不可重复读。问题就在于读——之前两阶段封锁主要约束事务读db的写是可串行化的。
二级封锁,在一级封锁基础上,在读取前需要加slock,读取后可释放。不脏读,但不可重复读。
三级封锁,在一级封锁基础上,在读取前需要加slock,事务结束时才可以释放。可重复读。——没有提及幻读,因为排除幻读需要gap lock。
======================
以上是封锁策略,使用三级封锁协议可以完美实现事务的正确读写,可串行化。可以使用MVCC,snapshot read和current read,用户可以决定是否使用snapshot read(如果在事务中使用简单的select 没有类似for update或者share lock)。也可以设置db事务隔离级别为serializable,也就是不允许snapshot read,都是current read,并且是三级封锁,(两阶段锁协议自不必说也是必须遵守的)。
serializable隔离级别的区别就是后者不存在snapshot read。
repeatable read比三级封锁协议更强,可以排除“脏读”,“不可以重复读”,“幻读”。因为使用gaplock
read committed对应二级封锁,但是不能排除“不可重复读”
read uncomitted对应一级封锁,但是不能排除“脏读”,“不可重复读”
======================
所以multi-version并不是每个事务都有一个版本,并发事务只要是当前读,仍然是请求锁,按顺序执行相关步骤。
======================
对于无索引的字段的条件过滤,如果是rc隔离级别那么就是全表扫描,最终对复合条件的记录加xlock;如果是rr隔离级别,则所有gap加gaplock,所有记录加xlock。如果是主键或者唯一索引或者是非唯一索引,需要加gap lock时,只在索引表加gaplock,clustered index表不加gaplock,xlock则是index表和clustered index表都要加的。