mysql mvcc原理

125 阅读3分钟

1.概念

mvcc( muti version concurrency control), 多版本并发控制 , RC和RR隔离级别是基于mvcc机制 ,数据库中的某条记录同时具有多个版本,在事务进行操作中时根据事务id以及隔离级别去判断读取哪个版本. mvcc实现了读写并发,相较于加锁,整体效率得到提升.

2.涉及知识点

2.1 undolog & 版本链

当对一条数据进行修改时,会生成一个与修改相反的日志以便进行数据还原, 即undo log

每次修改都会记录undolog 节点的指针, 多个版本通过回滚指针链接在一起,形成一个链表,即版本链

image.png

undolog 主要用于:

  1. 事务回滚
  2. 快照读

2.2 隐式字段

每条数据都有3个隐式字段

row_id: 当数据记录无主键时会使用row_id代替主键

trx_id: 最新事务id

roll_pointer: 回滚指针,指向undolog

2.3 快照读&当前读

快照读: 读取数据的可见版本, 不加锁

当前读: 读取数据的当前版本(最新), 加锁

select * from t lock in share mode; //共享读锁
 select * from t for update;  //写锁

2.4 read view

事务中执行sql语句则会开启一个read view (RC级别下,每个sql开启一个readview, RR级别下,每个事务开启一个readview) ,其主要用来做可见性判断的

image.png

  • m_ids:当前系统中那些活跃(未提交)的读写事务ID, 它数据结构为一个List。
  • min_limit_id:表示在生成Read View时,当前系统中活跃的读写事务中最小的事务id,即m_ids中的最小值。
  • max_limit_id:表示生成Read View时,系统中应该分配给下一个事务的id值。
  • creator_trx_id: 创建当前Read View的事务ID
//可见性判断,trx_id为版本链中某个版本的事务id
if(trx_id < min_limit_id) return true;  
if(trx_id >= max_limit_id) return false;
if(min_limit_id =<trx_id< max_limit_id){
	if(!m_ids.contains(trx_id)) return true; 
	else if(m_ids.contains(trx_id) && trx_id==creator_trx_id) return true;
	return false;

}

3. mvcc原理

3.1查询sql的流程

  1. 获取当前事务版本号(事务id),开启readview(readview 中记录m_ids,max_id,min_id以及creator_id)
  2. 查询数据以及数据的版本号(trx_id)
  3. 验证是否符合可见性原则,如果符合返回数据,如果不符合根据版本链找到上个版本继续验证

3.2 RC模式下与RR模式下处理可重复读

image.png

image.png

image.png

如上图所示,初始值为孙权,版本号100, 后事务B更新为曹操,版本号为101

在RC级别下

第一个select时,开启readview,参数如下:

变量
m_ids100,101
max_limit_id102
min_limit_id100
creator_trx_id100

最新数据版本为100, 根据可见性公式判断,数据可见,即第一次查询数据为孙权

第二个select时,开启readview,参数如下

变量
m_ids100
max_limit_id102
min_limit_id100
creator_trx_id100

最新数据版本为101,根据可见性公式判断,数据也可见,即第二次查询数据为曹操 此时, 不可重复读问题产生了

在RR级别下

第一个select时,开启readview,参数如下:

变量
m_ids100,101
max_limit_id102
min_limit_id100
creator_trx_id100

最新数据版本为100, 根据可见性公式判断,数据可见,即第一次查询数据为孙权

第二个select时,由于是RR级别,沿用之前的readview,参数如下

变量
m_ids100,101
max_limit_id102
min_limit_id100
creator_trx_id100

最新数据版本为101,根据可见性公式判断,数据不可见,即第二次查询数据为孙权