我们都知道数据库在并发同时执行的时候,就可能出现脏读、不可重复读、幻读的问题,为了解决这些问题,设置了四种事务的隔离级别,分别是
- 读未提交:A事务【能】看到B事务【未提交】的修改
- 读已提交:A事务【能】看到B事务【已提交】的修改
- 可重复读:A事务【不能】看到B事务【已提交】的修改
- 串行化:所有事务按顺序执行,不能并行
我们以Mysql为例,Mysql是怎么实现这些隔离级别的呢?
1. 通过数据库行锁实现
Mysql的行锁有两种,【读锁】(共享锁)和【写锁】(排它锁)。如果有一行数据如果加了【读锁】,那么这一行数据直到锁释放都加不了【写锁】(阻塞)。这一行加【写锁】的话,那么【读锁】和【写锁】都加不了。
我们假设只用这两种锁来解决上面的问题。那么
我们可以看到,加锁是可以实现上面的隔离界别的,但是有一个问题,就是数据库性能会急剧的下降
2. 通过MVVC实现
Innodb为了解决这个问题,引入了MVCC机制,既多版本并发控制。Mysql的MVVC是利用的回滚日志undolog完成的。这里简单讲一下Mysql的三种日志 redolog、binlog、undolog。redolog和binlog主要用于恢复数据,而undolog主要用于事务回滚。
假设一个值从1被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录
当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。
对于视图(read-view)A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。同时你会发现,即使现在有另外一个事务正在将 4改成5,这个事务跟 视图 A、B、C 对应的事务是不会冲突的。
这里只针对读的情况,对于写的情况还是会加写锁的,而普通读通过MVVC可以不用加读锁了,这样读跟写就不会互相阻塞了(写跟写还是会阻塞的)。另外Mysql的可重复读级别运用了间隙锁可以解决幻读的问题。