脏读:事务A读取到了事务B修改但未提交的数据。
不可重复度: 事务A查询同一条语句的前后结果不一样。
幻读:事务A条件查询同一语句前后两次结果不一样。
背景知识:读锁和写锁,在读数据时上读锁,在写数据时上写锁。
数据上读锁后不能被其他事务修改,知道读锁释放,数据上写锁其他事务不能读也不能修改。
读未提交:事务能读取其他事务未提交的数据。不检查任何锁
三种并发问题都没结局。
读已提交(锁在读后释放):事务只能读取其他事务已经提交的数据。
解决脏读。
可重复度(默认隔离等级)(锁在事务后释放):事务只能读取其他事务已经提交的数据。
解决不可重复读。
串行化:将事务强制以穿行的方式执行,就不会有并发发生。也使得数据库效率极低。
刚才在上边讲的读已提交和可重复读都是已锁的方式实现的,MVCC是不加任何锁实现这两种隔离等级,并且MVCC在读已提交等级下解决的大部分幻读问题。因为没有锁,所以效率极高。
MVCC是通过Read View + undo log 实现的
在每条数据后边都会有这两个隐藏列。
trx_id表示该条数据的版本号。
roll_pointer像一个指针,指向该条数据上一次的版本。
每个事务都会按顺序递增的分配一个版本号,通过此事务修改的数据都会把数据的trx_id修改成当前版本号。
保证原子性和一致性,用于MVCC的快照读(后边有介绍快照读)。
先来介绍两个概念,快照读和当前读
- 快照读:能从undo log中读取数据,也就是说能读取旧数据。不加锁,普通的select都是快照读。
- 当前读:读取的记录是最新版本,显示加锁都是当前读。
read view:在读已提交模式下,每条SQL语句生成一个读视图。在可重复读模式下,一个事务用一个读视图。用来判断当前事务可见的该条数据的版本。
Read View的几个变量,通过这些变量来确定数据是否可见。
Read view 匹配条件规则如下:
查询流程
1. 开始前系统顺序分配事务自己的版本号,即事务ID
2. 获取Read View
2. 查询得到的数据,然后Read View中的事务版本号进行比较。
3. 如果不符合Read View的可见性规则, 即就需要Undo log中历史快照;
4. 最后返回符合规则的数据
MVCC实现读已提交:
MVCC实现可重复读:
MVCC在可重复读模式下避免了部分的幻读:
如果是快照读数据,是不会发生幻读的。
如果是当前读,就有可能发生幻读,因为当前读都是读取最新的版本数据,Read View的可见性匹配规则就是功能。