携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
1.概念
MVCC(Multiversion Concurrency Control),多版本并发控制,通过管理数据行的多个版本来实现数据库的并发控制。MVCC使得在事务的隔离级别下可以实现一致性读。
2.快照读与当前读
2.1.快照读
又叫一致性读,就是不加锁的非阻塞读,不加锁的简单的SELECT都属于快照读,它能提高并发性能,避免加锁,降低了开销。
快照读是基于多版本的,读取到的不一定是数据的最新版本;
快照读要求隔离级别不是串行级别,否则会退化为当前读。
2.2.当前读
读取记录的最新版本,同时保证其他并发事务不能修改当前记录,对读取的记录加锁。加锁的SELECT或者对数据进行增删改进行当前读。
3.隔离级别
在MySQL中,默认的隔离级别是可重复读,从定义上看不能解决幻读,需要上升到串行化,但是会大幅降低事务并发能力。
采用MVCC可以不采用锁机制,通过乐观锁的方式解决不可重复读和幻读,可以在大多数情况下代替行级锁,降低系统开销。
4.隐藏字段与版本链
4.1.隐藏字段
trx_id:每次一个事务对行数据进行改动时,把该事务的事务id赋给这条记录的trx_id字段roll_point:每次事务对行数据进行改动时,会把旧的版本写入UNDO日志中,roll_point指针指向该日志,用来找到之前的版本信息
4.2.版本链
每次对记录的改动都会生成一条UNDO日志,每条日志也有也有roll_point属性,这些UNDO日志连起来构成了版本链,头结点是最新的版本。
5.ReadView
5.1.概念
ReadView就是事务在使用MVCC机制进行快照读操作时产生的读视图。
当事务启动时,会生成当前数据库系统的一个快照,InnoDB为每个事务构造了一个数组,记录了当前活跃事务的ID。
在MVCC机制中,多个事务对同一个行记录进行修改时会产生多个历史快照,都保存在UNDO LOG里。如果一个事务查询这条行记录,就需要用ReadView来判断读取哪一个版本的行记录。
5.2.设计思路
在READ_COMMITTED和REPEATBLE READ隔离条件下,事务只能读已经提交的事务修改过的记录,事务在未提交的情况下,ReadView需要判断版本链中的那条数据是当前事务可见的。
ReadView中4个重要的内容:
creator_trx_id:创建这个读试图的事务的ID(只读事务的ID为0)trx_ids:当前活跃的读写事务的ID列表up_limit_id:活跃的事务中最小IDlow_limit_id:下一个事务应该分配的ID
要区分活跃事务与已提交事务
5.3.规则
如果被访问的版本的trx_id属性值:
-
等于ReadView中的
creator_trx_id,说明当前事务在访问它自己修改的记录,可以访问 -
小于ReadView中的
up_limit_id,说明生成该版本的事务已经提交,可以访问 -
大于或等于ReadView中的
low_limit_id,说明生成该版本的事务在当前事务之后才启动,不能访问 -
在
up_limit_id和low_limit_id之间,则判断是否在trx_ids中:- 若在,则说明仍在活跃的事务,不可访问
- 若不在,则说明事务已提交,可以访问
5.4.流程
当要获取一条记录时:
- 获取事务ID
- 获取ReadView
- 查询得到的数据,与ReadView中的事务版本号进行比对
- 如果不可访问就获取历史快照
- 返回可以访问的数据
如果所有版本都不可访问,则查询结果不包括该记录。
- 在
READ COMMITTED中:
事务每进行一次查询操作都会重新获取一次ReadView,可能产生不可重复读和幻读的情况
- 在
REPEATABLE READ中:
事务只在第一次查询时获取一次ReadView,后面都复用这个读试图,所以避免了不可重复读(所以同时也解决了幻读)
5.5.实例
1.READ COMMITTED
- 现有活跃事务10、20,新事务30读操作:
ReadView:
creator_trx_id:30 ;trx_ids:{ 10,20 };up_limit_id:10;low_limit_id:21
SELECT * FROM TABLE WHERE NUMBER = 1;
读出记录:‘张三’
- 提交事务10,事务20更新记录,新事务30读操作:
ReadView:
creator_trx_id:30 ;trx_ids:{ 20 };up_limit_id:20;low_limit_id:21
读出记录:‘王五’
2.REPEATABLE READ
- 现有活跃事务10、20,新事务30读操作:
ReadView:
creator_trx_id:30 ;trx_ids:{ 10,20 };up_limit_id:10;low_limit_id:21
读出记录:‘张三’
- 提交事务10,事务20更新记录,新事务30读操作:
ReadView:
creator_trx_id:30 ;trx_ids:{ 10,20 };up_limit_id:10;low_limit_id:21
读出记录:‘张三’