事务并发的问题有四种分别是脏写、脏读、不可重复读、幻读。其中脏读、脏写是在事务并发中读取到了未提交事务修改过的数据,这会导致比较严重的问题。
问题 | 描述 |
---|---|
脏写 | 一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了脏写 |
脏读 | 一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读 |
不可重复读 | 一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读 |
幻读 | 一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读 |
MySQL的默认隔离级别为REPEATABLE READ。
MySQL设计实现版本控制,主要通过InnoDB page记录中的字段和Undo Log实现版本链记录,结合ReadView 事务id的比较实现版本控制。
trx_200需要等待trx_100更完毕后才能更新是因为:InnoDB使用锁来保证不会有脏写情况的发生,也就是在第一个事务更新了某条记录后,就会给这条记录加锁,另一个事务再次更新时就需要等待第一个事务提交了,把锁释放之后才可以继续更新。
有了版本链的记录,MySQL就可以通过当前事务id判断是在提交前读取记录,还是提交之后读取记录了。所以就有了ReadView的概念,实现版本控制
这个ReadView中主要包含4个比较重要的内容:
ReadView进行版本控制主要是对可重复读和读已提交的隔离级别设计的,因为在读未提交直接读取最新版本即可,可串行化则是直接加锁访问。
而在READ COMMITTED和REPEATABLE READ隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的。
所以在判断READ COMMITTED 和REPEATABLE READ 实现版本控制,只要读事务在生成ReadView的记录活跃ids不同即可。
在 READ COMMITTED中,开启两个事务trx_100的事务先修改记录,查询记录则ids=[100,200],然后提交trx_100事务。然后trx_200开始修改记录,查询记录生成ReadView的ids=[200],所以可以读取到trx_100提交的记录。前后读事务生成了两次ReadView。
在REPEATABLE READ 中,开启两个事务trx_100的事务先修改记录,但只是在第一次生成ReadView。第二次读事务则不再生成新的ReadView。所以两次查询的接口都是一致的。