MVCC(多版本并发控制,Multiversion Concurrency Control)是一种数据库并发控制的策略,指维护一个数据的多个版本,使得读写操作没有冲突。
假设现在同时有四个事务,对同一张表的同一行记录进行操作:
如图,纵轴为时间线,当前事务5中的两次查询查询的分别是哪个事务版本的记录呢?想确定这个问题就需要用到MVCC了,MVCC最主要的功能就是确定在多个事务并发的情况下,到底该访问哪个版本。
MVCC的具体实现,主要依赖于数据库记录中的隐式字段、undo log日志、readView。
MVCC实现原理
我们先来说隐藏字段:
隐藏字段
这里有一张表,表中有id,age,name三个自定义的字段。其实这张表内部还含有三个MySQL提供好的字段:
- DB_TRX_ID:最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,自增
- DB_ROLL_PTR:回滚指针,指向这一条记录的上一个版本,用于配合undo log,指向上一版本
- DB_ROLL_ID:隐藏逐渐,如果表结构没有指定主键,将会生成该隐藏字段
undo log
当insert的时候,产生的undo log只在回滚时需要,在事务提交后,可被立即删除。而update,delete的时候,产生的undo log日志不仅在回滚时需要,MVCC版本访问也需要,不会被立即删除。
undo log版本链
不同事务或相同事务对同一条记录的修改,会导致该记录的undolog生成一条记录版本链表,链表头部是最新的旧记录,链表尾部是最早的旧记录。
readview
ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
当前读:读取的是最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select...lock in share mode(共享锁),select...for update,update,insert,delete(排他锁)都是一种当前读。
快照读:简单的select(不加锁)就是快照读,读取到是记录数据的可见版本,也可能是历史数据,不加锁,是非阻塞读。
- Read Committed:每次select,都生成一个快照读。
- Repeatable Read:开启事务后第一个select语句才是快照读的地方。
ReadView中包含了四个核心字段:
字段 | 含义 |
---|---|
m-ids | 当前活跃的事务ID集合 |
min_trx_id | 最小活跃事务ID |
max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID自增) |
creator_trx_id | ReadView创建的事务ID |
ReadView为了哪个获取快照读中最准确的数据,定义了一些数据访问的规则:
trx_id代表当前事务ID。这个事务ID也可能是历史版本的事务ID。
不同的隔离级别下,生成ReadView的时机不同:
- READ COMMITED:在事务中每一次执行快照读时生成ReadView
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView