MySQL事务与MVCC
大约 5 分钟
什么是事务?
事务就是多条SQL语句,要么全部成功,要么全部失败
事务的4大特性:ACID,即原子性,一致性,隔离性,持久性
- 原子性:一个事务中的所有操作,要么全部成功,要么全部失败
- 一致性:事务让数据库从一个一致性状态到另一个一致性状态,数据库的状态与业务规则是保持一致的,比如转账,不论怎么操作,钱的总额是不变的
- 隔离性:事务与事务之间是隔离的,一个事务不会影响另一个事务
- 持久性:事务提交后,对数据库的改变应该是持久的,即使发生崩溃也能够恢复
事务并发带来的问题
- 脏读:一个事务对数据进行了修改,但是并没有提交,在另一个事务中能够看到这种修改,称为脏读
- 丢失修改:一个事务中对数据进行了修改,另一个事务中也对这个数据进行了修改,导致第一个事务的修改结果被覆盖掉了,称为丢失修改
- 不可重复读:在一个事务中重复读同一个数据时,因为另一个事务修改了这个数据,导致前后两次重复读取的结果不同,称为不可重复读
- 幻读:一个事务在读取数据时,另一个事务插入或者删除了一些记录,导致这个事务在读取的时候发现多了或者少了一些记录,像是产生幻觉一样,称为幻读;幻读产生的原因是因为某个事务读取了某个范围的记录,之后其他事务在该范围内插入了新的记录,该事务再次读取这个范围的记录时,可以读取到新插入的记录
不可重复读和幻读的区别:二者的侧重点不同,不可重复读侧重于读取一条记录,发现这条记录的某些列不一样;幻读侧重于插入或者删除一些记录
事务的隔离级别
- 读未提交:允许读取到其他事务修改了但未提交的数据
- 读已提交:允许读取到其他事务已经提交的数据
- 可重复读:对同一字段的重复读取的结果是一样的,除非数据被本事务进行了修改,默认隔离级别
- 串行化:所有的事务依次逐个执行,事务之间不会有干扰

什么是MVCC?
MVCC,多版本并发控制,是一种用来解决读写冲突的无锁并发控制,在并发读写数据库时,可以做到读操作不用阻塞写操作,写操作不用阻塞读操作,提高了数据库并发读写的能力,同时还可以解决脏读,幻读,不可重复读等问题
MVCC实现原理
为什么说MVCC是无锁的并发控制呢?这也MVCC的实现原理有关,MVCC的实现依赖于数据库的隐藏字段,undolog,read view隐藏字段:
- 隐藏主键:如果数据没有主键,MySQL会自动生成一个主键
- 回滚指针:指向这条记录的上一个版本,配合undolog使用
- 最近修改事务id:记录创建这条记录或者最后一次修改这条记录的事务的id

read view
read view是事务进行快照读(普通select语句)的时候产生的读视图,它用来记录并维护系统当前活跃的事务id(还没提交的事务的id) read view最大的作用是用来做可见性判断,当事务进行快照读的时候,对该记录生成一个read view,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能看到的是最新的数据,也有可能看到的是一个历史版本
read view有三个重要的属性:
- 事务id列表:read view生成时刻,系统中活跃的事务id
- 最小事务id:事务id列表中最小的事务id
- 最大事务id:read view生成时刻,尚未分配的下一个事务id
事务1 | 事务2 | 事务3 | 事务4 |
---|---|---|---|
开始 | 开始 | 开始 | 开始 |
...... | ...... | ...... | 修改且已提交 |
进行中 | 快照读 | 进行中 | |
...... | ...... | ...... |
上面的例子中,事务2为当前事务,则当事务2进行快照读,生成read view时,各个属性都列举如下:
- 当前事务id:2
- 最近修改事务id:4
- 事务id列表:1,3
- 最小事务id:1
- 尚未分配最大事务id:5
read view比较规则:
- 如果最近修改事务id<最小事务id,则当前事务能看到最近修改事务id所在的记录,否则进入下一个判断
- 如果最近修改事务id>=尚未分配最大事务id,则代表最近修改事务是在read view生成之后才出现的,那么当前事务肯定不可见,因为当前事务版本更老,否则进入下一个判断
- 判断最近修改事务id是否在事务id列表中,如果在,则说明read view生成时刻,最近修改事务还没有提交,仍然处于活跃状态,是不可见的,如果不在,则说明最近修改事务在read view生成之前就已经提交了,是可见的
RC,RR级别下快照读的区别
在读已提交和可重复读级别下,read view生成的时机不同,从而造成快照读结果不同
- 在RC隔离级别下,每个普通select语句(快照读)都会生成一个read view,这也是在RC隔离级别下,能看到其他事务提交的更新的原因
- 在RR隔离级别下,第一个快照读语句会生成一个read view,之后的查询操作都重复使用这个read view