select+update引发的一些思考(2)顺藤摸瓜

Q:

  1. UPDATE的行锁问题?
  2. commit之前有所误解,事务提交和数据写入有所弄混(redo、undo、写入磁盘等)
  3. 由上述问题引发事务隔离与锁的问题
  4. 二阶段锁定和那些事务执行中的锁 概念有所弄混

隔离性

感觉上述问题,第一个就要谈到Isolation(隔离性)

Isolation:也意味着可串行化。 每个事务都像是唯一具有RD / WR共享全局状态的事务一样执行。

这是在cmu440ppt上的解释,可串行化?意味着可以顺序执行,互不干扰。
如果这样那像上一章的并发事务导致更新覆盖的问题就不会出现,还有二阶段锁是为了保持事务的原子性和隔离性,使之串行化。
cmu440关于二阶段锁

二阶段锁:
事务分为两个阶段:
第一阶段是获得封锁,也称为扩展阶段;
第二阶段是释放封锁,也称为收缩阶段。

在释放一个封锁之后,事务不能再获得任何其他封锁。
如果所有的事务都遵守二阶段锁定协议的话,在事务之前获取锁,事务结束阶段释放锁,那么所有并发事务执行结果必然正确
所以感觉有所矛盾,可能事务不一定完全遵守二阶段锁,而且完全遵守二阶段锁效率是非常低下的。

很明显这在同时并发有多个事务要执行的环境下是没有执行效率的,一个事务的执行,必然会阻塞其他事务的执行。所以SQL的标准制定者对此作出妥协,提出隔离性的四个等级,其中最高级隔离等级才是串行执行。

隔离性的4个等级

这里谈到了为了妥协,出现了事务隔离的等级,之前有篇文章谈到:
脏读、不可重复读、幻读

  1. 未提交读(read uncommitted)读到了未提交而修改了的数据。A事务修改了某个字段数据C,还未提交,而B事务读取了修改了的数据C,最后A事务rollback,C恢复到原来的状态,这种情形就叫脏读属于完全不隔离。

  2. 提交读(read committed)比起未提交读高一等级,要等到其他事务更新完毕后,才可以进行读。但只有没有达到串行化的级别,依旧会有问题。当A事务可能对一个数据进行两次读写,而B事务在两次读写之间进行了修改,导致A的用户两次读写不一致,这成为不可重复读。对于一个事务本身,它所感知的数据库应该它自己操作,但是这种情况A事务并没有更新数据,而发生了改变。

  3. 可重复读(repeatable read) 提交读会出现不可重复读的情形,RR隔离级别在此基础上,保证在一个事务内两次读取不会不一致。但是如果是INSERT操作,在这个隔离级别依旧会出现问题:事务A开启事务,并select一段有范围的数据,然后事务B开启事务,在先前A事务select的那段有范围的数据中insert一条数据,然后提交事务,接着事务A再select出来这段数据,发现数据多了一条,这种情况叫幻读

  4. 序列化读(serializable)这也就是最高级别,保证事务之间不会有任何踩踏,每个事务都可以认为只有它自己在操作数据库。

q:这些隔离级别都跟上锁的不同机制相关,那这些锁跟二阶段锁定的锁之间的联系呢?update时操作的锁?

隔离级别的实现(锁、封锁协议)

锁的类别

  1. 读写锁,事务读的时候加上读锁,允许多个事务上读共享锁,只读不写(Java的ReadWriteLock),但任何事务都不能加上其他的锁,直到它释放为止。

  2. 排他锁(写),类似java的Reentrantlock,被加锁的对象只能被持有锁的事务读取和修改,其他事务无法在该对象上加其他锁,也不能读取和修改该对象

封锁协议

事务在不同时机给数据上锁,来实现各种不同的隔离级别。

  1. 一级封锁协议 (对应 read uncommited)B事务在修改数据的瞬间上了排他锁,直到事务结束才会释放,而读取不需要加上共享锁就可以读取,A事务只能读取,无法修改(导致脏读)
  2. 二级封锁协议 (对应read commited) B事务在修改的瞬间上了写锁,直到事务结束才释放,A事务才可以读取;A事务对当前被读取的数据上面加共享锁(当读到时加上共享锁),一旦读完该行,立即 释放该 该行的共享锁。避免了脏读,但无法避免幻读、不可重复读。

如果要避免 丢失更新,我们需要额外的操作, 对凡是读到的数据加 共享锁 和排他锁 ,这个往往需要程序员自己编程实现,比如在Oracle 中,需要加 SELECT FOR UPDATE 语句,表明,凡是该事务读到的数据,额外的加上排他锁,防止其他数据同一时间获取相同数据,这样就防止了 丢失更新 !

  1. 三级封锁协议 (对应reapetable read )二级封锁协议加上事务,A事务读取数据的时候就加上共享锁,一直等到事务结束才释放,其他事务可以读但并不能修改(直到A事务结束才可以)。

q:二阶段锁和三级封锁协议关系

两类不同目的的协议
两段锁协议:保证并发调度的正确性
三级封锁协议:在不同程度上保证数据一致性
遵守第三级封锁协议必然遵守两段协议

三级封锁协议没有脏读、更新覆盖、由于该封锁协议只是对某行数据加锁,因此在事务 A 没有完成之前,事务 B 可以新增数据(因为不是对锁住的这一行操作),那么 当事务 A 再次读取的时候,事务B 新增的数据会被读取到,这样,在该封锁协议下,幻读 就产生了。

  1. 最强封锁协议(对应Serialization)
    四级封锁协议是对三级封锁协议的增强,其实现机制也最为简单,直接对 事务中 所 读取 或者 更改的数据所在的表加表锁,也就是说,其他事务不能 读写 该表中的任何数据。这样所有的 脏读,不可重复读,幻读 ,都得以避免!

在SELECT 的查询中使用一个“WHERE”子句来描述一个范围时应该获得一个“范围锁”(range-locks)。

你可能感兴趣的:(select+update引发的一些思考(2)顺藤摸瓜)