事务隔离级别

309 阅读3分钟

参考资料

dev.mysql.com/doc/refman/…

tech.meituan.com/innodb-lock…

hedengcheng.com/?p=771

hedengcheng.com/?p=577

en.wikipedia.org/wiki/ACID

先解释acid,根据wiki上的解释如下:

a: 原子性,all or nothing,一个事务的操作,要么全部发生,要么全部不发生,如果在中间发生了错误,那么会回滚之前的操作。

c: 一致性,事务必须保证数据库约束的正确性,比如唯一索引,外键等约束,状态必须是一致的。

i: 隔离性,允许多个事务并发的读取和修改数据,就好像这些事务是顺序执行的一样。能够让每个事务所看到的数据都是一个一致状态下的,根据不同的需求有不同的隔离级别,除了ru级别之外,rc和rr以及s级别都能保证事务看到的数据是完整的,但是不保证是最新的。

d: 持久性,事务结束后,事务对数据所做的修改是持久的,即使系统故障也不会丢失!

下面重点说一下在不同的隔离级别下的区别:

ru:在ru级别下一个事务可以读到另外一个事务没有提交的数据,这样会造成脏读,不可重复和幻读!

rc:在rc级别下一个事务可以读到另外一个事务提交的数据,这样可能会造成不可重复读和幻读,因为另一个事物提交后,修改的数据突然变得可见了。主要针对update和delete

rr:在rr级别下一个事务可以重复读取数据。但是可能存在幻读,另外一个事物突然插入了数据。主要针对insert

s: 串行之行,读和读也会加锁!

在mysq innodb的实现中:

  1. 对数据的写实际上最开始都是对数据的一个当前读,所以写可以当作当前读来看。
  2. 由1可知,在innodb中分为两种读,一种是当前读,另外一种是快照读。当前读读取的是最新的数据,所以需要加锁来排除别的事务对数据的同时更改,如果有别的事务在更改相同的数据就会阻塞。快照读不需要加锁,读取的是数据的快照。
  3. 不管在哪个隔离级别下,当前读和当前读都是冲突的,快照读和快照读都是不冲突的,当前读和快照读是不会冲突的,快照读产生的问题就是可能读取到的不是最新的数据,但是能保证是完整的数据。

快照读在rc和rr级别下的实现:

  1. rc级别解决了脏读,读不到别的事务没有commit的脏数据,根据readview技术,在每次读数据的时候都创建一个readview,实际上是比较数据的版本号和当前正在进行的数据集合进行判断!

  2. rr级别解决了不可重复读和幻读(通过mvcc),实际上是根据readview技术来的,在每次事务开始的时候创建一个readview,这样后面即使事务提交了,也不影响readview,达到重复读的效果!

当前读在rc和rr级别下的实现:

  1. rc级别解决了脏读和不可重复读。通过加record lock解决了脏读和不可重复读的问题,具体是读取数据的时候就加上了record lock,所以别的事务就没法更改数据了。但是解决不了幻读,因为没有加gap锁,所以别的事务还是可以通过insert插入满足条件的数据,造成幻读。
  2. rr级别解决了不可重复读和幻读(通过next-key lock,也就是record lock和gap lock的统称),通过加上gap lock,这样别的事务的insert就没办法插入满足条件的纪录,也就不存在幻读!

大概就是这样!