文章目录
- 并发事会带来哪些问题?
- 如何解决幻读
- MVCC(多版本并发控制)
- MVCC主要作用(解决了什么问题):
- MVCC在可重复读隔离级别下Mysql是如何工作的:
- select操作:
- install操作:
- delete操作:
- update操作:
- 快照读与当前读:
- 脏读(Dirty read)
- 一个事务读取了另一个事务还没有提交的数据;
- 不可重复读(Unrepeatableread)
- 同一个事务多次读取一条记录,发现该记录中某些值被修改过;
- 幻读(Phantom read)
- 同一个数据在多次查询数据时,会出现数据量不一致的情况,也就是把其他事务插入的数据也查询到了;
其中幻读和不可重复读有些类似,区别:
- 幻读是指同样的一条查询语句,前后两次读取,数据的数量发生了改变;
- 不可重复读是指的同一批数据,前后两次读取,这一批数据的值发生了变化;
以上一笔带过,以下主角登场;
- 修改隔离级别改为可串行化,但是效率非常低,可串行化的意思就是说当你开启事务之后,其他事务都不可以进行操作了,只有当你提交完事务之后才可以操作,实际生产中不可能使用此方法;
- 使用(多版本并发控制)解决幻读;
- 只能在可重复读和读已提交两个隔离级别下工作,和其他隔离级别是不兼容的,因为读未提交总是读取最新的数据,而可串行化对所有数据都会加锁。
- 比单纯的加锁更高效,只在和两个隔离级别下工作
早期数据库不论读取还是写入,都用锁来实现。但是锁会带来性能的问题,MVCC就是MySQL对于读的一种优化方案。
在使用MVCC中,对于任何修改的操作,都不会直接覆盖掉原数据,而是产生一个新老版本共存的状态,根据版本号去区分,使得读取时可以完全不加锁,这样读取数据时,MVCC根据版本号就会判断出应该读取哪些数据,完全不用加锁,MVCC存在的意义就是使MySQL读写不冲突。
一句话概括:他就是一个行级锁的变种,读数据无需加锁、读写不冲突,系统开销更低。
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现。
- 一个保存了创建事务id
- 一个保存了删除事务id
在插入数据的时候,删除的事务id是空的
在执行删除操作时,mysql会在底层记录删除行的删除事务id,对于更新操作原数据并不会真正的被更新掉,只是会隐藏,然后再新增一条数据(暂时可以这么理解,其实底层是使用undo log完成的),mysql会在底层新增一行相同的数据,并记录好对应的创建事务id。
当然存储的并不是真实的时间而是系统版本号。每开始一个新的事务,系统版本号都会自动新增。事务开始时刻的系统版本号会作为事务的版本号,用来查询到每行记录的版本号进行比较。
开始新一个事务时,该事务的版本号肯定会大于当前所有数据行快照的创建版本号
select操作:
InnoDB只查找版本早于当前事务版本的数据行,这样就可以确保查询到的数据在此事务操作之前就已经存在了,要么是事务自身插入或者修改过的,会过滤掉此事务开始之后的数据。
行的删除版本号要么未定义,要么大于当前事务id,确保事务读取到的行在事务开始前未被删除(如果事务开始之前被删除的话,删除事务id就会小于当前创建事务id,这些数据会被过滤掉),但在事务开始之后删除的数据同样也会被查询到。
简化一下就是这样子:
事务id <= 当前操作事务id && (删除id > 当前事务id || 删除id == null)
只有符合上面两个条件的数据才会被查询出来。
install操作:
增加创建版本号;
delete操作:
增加删除版本号;
update操作:
增加创建版本号;
通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,不是数据库最新的数据。这种读取历史数据的方式,我们叫它快照读,而读取数据库最新版本数据的方式,叫当前读;
对于select操作会采用快照读;
对于(install、update、delete)操作会采用当前读;