ITPub博客

首页 > 数据库 > Oracle > 事务隔离级别读书笔记分享

事务隔离级别读书笔记分享

原创 Oracle 作者:wei-xh 时间:2018-04-25 14:09:10 2 删除 编辑
  1. 在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。快照读,某一时刻的一致性读,不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

  2. MySQL在RR隔离级别下,快照读和当前读如果在一个会话中先后出现,可能会出现幻读。因为快照读不加锁,会允许新的插入,当前读需要读到块的最新版本,因此快照读和当前读两次操作间,就可能会出现幻读。 https://www.cnblogs.com/crazylqy/p/7612230.html
  3. MySQL为实现RR隔离级别,带来了很大的代价,引入了Next-Key Locking解决当前读模式下的幻读问题。Next-Key Locking可能会导致大量的DML失败。 https://www.cnblogs.com/crazylqy/p/7689447.html,Oracle没有RR隔离级别,在read only级别下,Oracle没有幻读的问题(是快照读的模式),在read committed级别下,快照读、当前读以及混合的【快照读和当前读】下都存在幻读的问题。 https://www.cnblogs.com/crazylqy/p/7614092.html
  4. RR隔离级别,MySQL是依靠MVCC实现的可重复读(read view),同时依靠MVCC实现快照读下的幻读问题,依靠Next-Key Locking实现当前读下的幻读问题,所以MySQL InnoDB的可重复读并不保证避免幻读,需要应用显式的使用加锁读来保证,而这个加锁读使用到的机制就是next-key locks。
  5. 脏读、不可重复读、幻读,是一种缺陷,越往后,解决缺陷的成本越高
  6. 隔离级别越高,能解决的“缺陷”越多,为什么不直接使用最高的事务隔离级别,那不就没有缺陷了? 因为隔离级别越高,并发性可能会越低。
  7. 脏读,解决 写不阻塞读的问题,提高并发性,牺牲读一致性。现在绝大多数的主流数据库,都是通过MVCC来解决写不阻塞读的问题,不是通过经典的锁来实现。
  8. 为什么会出现不可重复读取? 
    在经典的read commited方式下,对于读取过的数据并不加锁(读取的当下加共享锁,读取完成后释放共享锁),那么再次访问时数据可能已经被其他事务修改。
  9. 如何才能做到可重复读? 
    老一辈的数据库艺术家用的方式是加锁,对读取过的数据加锁,就能保证每一次读取到的数据都是一样的,因为对读取过的数据加锁后,数据无法发生修改了。这也是repeatable read这个隔离级别要解决的问题,但是经典的实现可重复读的方式会产生幻读。现在绝大多数的主流数据库,是通过MVCC来做到的可重复读,不是通过加锁。
  10. 经典的可重复读提供了一个一致性(语句级和事务级)的读取方式,虽存在幻读,单从一致性的角度看,并不是一个大的缺陷,如果以经典的锁的方式去实现可重复读,发生死锁的概率极大,但带来的一个(好的)副作用,解决了丢失更新的问题。
  11. 按照经典的隔离级别定义,read uncommited,read commited,都不能提供一致性读。因为当下主流数据库都基于MVCC实现,不基于经典的锁方式,所以都实现了在read commited级别下的语句级的一致性。经典的隔离级别下,repeatable read在语句级一致性的基础上还做到了事务级的一致性。
  12. 针对oracle的隔离级别来说,read commited级别能够提供语句级的一致性,这个隔离级别避免不了事务级的幻读的问题,需要read only或者最高的SERIALIZABLE级别。
  13. 在一个采用共享读锁(而不是多版本)的数据库中,如果启用了REPEATABLE READ,可以避免丢失更新的问题。原因是:已被读取的数据会在上面加一个锁(共享读锁,非排它锁),这个锁会保证数据不能被任何其他事务修改。
  14. 在可重复读(REPEATABLE READ,简称RR)隔离级别下,read view是在第一个读请求发起时创建的。在读已提交(READ COMMITTED,简称RC)隔离级别下,则是在每次读请求时都会重新创建一份read view。根据上面提到的说法,RC隔离级别下,是每次发起SELECT都会创建read view,也就是每次SELECT都能读取到本次查询开始时的已经commit的数据,所以才会出现不可重复读、幻读现象。
  15. read view 判断当前版本数据项是否可见 
    在innodb中,创建一个新事务的时候,innodb会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。当用户在这个事务中要读取该行记录的时候,innodb会将该行当前的版本号与该read view进行比较。 
    具体的算法如下: 
    1. 设该行的当前事务id为trx_id_0,read view中最早的事务id为trx_id_1, 最迟的事务id为trx_id_2.
    2. 如果trx_id_0< trx_id_1的话,那么表明该行记录所在的事务已经在本次新事务创建之前就提交了,所以该行记录的当前值是可见的。跳到步骤6.
    3. 如果trx_id_0>trx_id_2的话,那么表明该行记录所在的事务在本次新事务创建之后才开启,所以该行记录的当前值不可见.跳到步骤5。
    4. 如果trx_id_1<=trx_id_0<=trx_id_2, 那么表明该行记录所在事务在本次新事务创建的时候处于活动状态,从trx_id_1到trx_id_2进行遍历,如果trx_id_0等于他们之中的某个事务id的话,那么不可见。跳到步骤5.
    5. 从该行记录的DB_ROLL_PTR指针所指向的回滚段中取出最新的undo-log的版本号,将它赋值该trx_id_0,然后跳到步骤2.
    6. 将该可见行的值返回。

需要注意的是,新建事务(当前事务)与正在内存中commit 的事务不在活跃事务链表中。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/22034023/viewspace-2153328/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
Oracle ACE组成员,DBGeeK用户组发起人。曾在DTCC、ORACLE技术嘉年华、Gdevops等公开场合做过数据库技术专题分享,2017年应Oracle邀请在世界最大的数据库会议OOW上做技术分享。组织翻译了《拨云见日,解密Oracle ASM内核》一书。

注册时间:2009-07-04

  • 博文量
    422
  • 访问量
    2425305