MySQL实现了四种隔离级别,分别是读未提交(RU)、读已提交(RC)、可重复读(RR)和串行化读(SERIALIZABLE)。
读未提交(RU):不管其他事务是否提交更新都可以实时看到最新数据,隔离级别最低,脏读的问题没有解决,更别说可重复读和幻读了。
读已提交(RC):只可以看到其他事务已经提交事务后的更新,解决了脏读的问题,但没有解决可重复读和幻读。
可重复读(RR):在事务中的任何时间点看查询到的数据都是一样的,解决了脏读和可重复读的问题,但没有解决幻读的问题。
串行化读(SERIALIZABLE)所有数据库请求都只用一个线程执行,不管读写并发的事情,所以在这个隔离级别下隔离效果最好,性能最差。
相关概念解析
脏读:同时两个事务A和B, A修改了记录1 ,同时B读取了记录1,此时A记录回滚,那么B读到的就是一条未提交的脏数据,RU级别就会出现脏读。
可重复读:同时两个事务A和B, 不管A做了什么修改,B在任何时间段读去的内容都是一样的。
幻读:一个事务在不同的时间点使用相同的查询条件,查询到的记录行条数不一致的情况。
脏读和可重复读描述的都是已存在的数据,幻读描述的是新增的记录行。
MySQL如何实现RC和RR级别?
MySQL实现了 MVCC (Multiversion Concurrency Control) 中文全称叫多版本并发控制,是一种快照读机制,普通的select查询就是快照读,MySQL使用MVCC实现了RC和RR级别的隔离。MVCC机制下,会给每一个记录行额外维护两个字段DATA_TRX_ID(最新更新事务ID)和DATA_ROLL_PTR(上一版本指针)。在这种机制下,在查询的时候会生成一个readView表,维护当前系统中活跃的事务ID, 记作m_ids,其中最小值为 low_limit_id,最大值为 up_limit_id,g行记录的transID记为trx_id,行记录的可见性如下。
1、读取的记录版本trx_id < low_limit_id 那么该版本对于m_ids中的事务都可见;
2、trx_id > up_limit_id则表示该版本对于m_ids中的事务都不可见,需要查询上一版本再判断其可见性;
3、low_limit_id <= trx_id <= up_limit_id,此时如果trx_id 在m_ids中,则当前版本不可见,需要查询上一版本再判断其可见性,如果不在m_ids中则表示该事务已经提交,所有m_ids中的事务都可见。
RC和RR两种隔离级别的快照读,除了生成readView表的时机不一样,可见性等其他都是一样的。RC级别下会在每次select生成readView, 而RR级别下是在首次select时生成的。
RR级别下是否解决了幻读?
分情况对待。单纯的快照读和当前读下解决了幻读,但是在既有快照读和当前读的情况下没有解决。
快照读:不带任何锁的select语句
当前读:select for update、select in share mode 、 update 、 insert、 delete
单纯的快照读是指一个事务只有简单的快照读的情况是解决了幻读的问题,因为在首次select时readView就已经确定。
当前读的情况下也是解决了幻读的问题,事务会给索引加记录锁和间隙锁,其他事务是不能插入新的记录行的。
在既有快照读和当前读的情况下没有解决幻读的,因为快照读是查询不到新增记录的,而当前读会查询到最新添加的记录。
详细可见Mysql MVCC。