Mysql和其他大多数数据库中的事务型存储引擎的实现一般都不是简单的行锁。一般基于提升并发性能考虑,他们一般都会实现多版本并发控制(MVCC),他们实现的机制都不太一样。

这里可以将MVCC看做成是一个行锁+读锁的一种合理的组合方式。(但本身是没有进行加锁的操作的)

MVCC的实现是通过保存某个时间点的快照(ReadView)来实现的。

————————————————————————————————————————————————————————

ReadView解析

m_ids

表示当前还没提交的事务id的列表

min_trx_id

表示列表中最小的事务id

max_trx_id

表示列表中最大的事务id+1(即:即将分配的事务id)

creator_trx_id

表示当前事务的事务id

ReadView判断版本链中可用的判断依据

  • trx_id == creator_trx_id ——》 可以访问的版本(代表自己建立的事务版本)
  • trx_id < min_trx_id——》 可以访问这个版本(代表已经提交过的事务版本)
  • trx_id > max_trx_id ——》 不可以访问这个版本
  • min_trx_id <= trx_id <= max_trx_id ——》如果在m_ids(没有提交的事务id)中则不可以访问,如果没在则事务的线程已经超车完成提交了事务,则可以访问

注:trx_id是访问的事务版本id,当是select查询语句的时候id并不是为零,会有自己的一套生成规则。(听大佬的参考:《mysql是怎样运行的》,自己不摸鱼以后过来重新补充改改)

————————————————————————————————————————————————————————

拿InnoDB引擎的MVCC来说,它是通过在每行数据的后面加上隐藏的三列来实现的,即

row_id:确认当前行唯一用的,有主键或者唯一索引的时候不生成。

transaction_id:事务id,就是当前行的事务版本号。

roll_pointer:回滚指针,指向undolog记录的当前数据的上一个版本的指针。(undolog会记录很多个版本,一个版本的指针指向上个版本,会形成一个链,就叫版本链)

每当有一个新的事务开启的时候,就会有一个与其对应的事务版本号,系统版本号都会递增。这里面我们拿repeatable read(可重复读)隔离级别下的MVCC操作来举个例子:

Insert操作:为新插入的每一行保存当前系统版本号作为当前行的版本号

Delete操作:为删除的每一行保存当前系统版本号为当前行的版本号

Update操作:为插入一行的新纪录保存当前版本号作为当前行的版本号

Select操作:也会生成一个对应的事务id,然后进行当前事务id的进行判断。

 

然后就会根据版本链中的对应的版本号进行数据读取。

MVCC只在可重复读和读已提交两个隔离级别下生效,其余隔离级别下是不需要的。