MVCC-InnoDB基于UndoLog的并发事务查询机制

1 并发事务带来的问题

当多个事务并发执行的时候就会导致事务并发问题包括脏写、脏读、不可重复读和幻读,而MVCC为了解决不可重复读和幻读。

1.1 不可重复读

同一个事务下,使用相同的查询语句,在不同时刻读到的结果不一致。

1.2 幻读

同一个事务下,两次读取一个范围的数据记录,两次读取的结果不同。

2 当前读&快照读

2.1 当前读

特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

它读取的数据库记录,都是当前最新的版本,会对当前读取的数据进行加锁,防止其他锁事务修改数据。是一种悲观锁操作。

select * from table where ? lock in share mode;共享锁

select * from table where ? for update;排它锁

insert into table values (…);排它锁

update table set ? where ?;排它锁

delete from table where ?;排它锁 

2.2  快照读

快照读的实现是基于多版本并发控制,即MVCC,既然是多版本,那么快照读到的数据不一定是当前最新的数据,有可能是之前历史版本数据。

单纯的select操作,不包括上述 select ... lock in share mode, select ... for update。   

3 事务隔离级别

不同事务隔离级别对问题的解决程度对比
事务隔离级别脏读不可重读读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复读不可能不可能可能
串行化不可能不可能不可能

4 UndoLog

MySQL中事务一致性由UndoLog来实现的,UndoLog在事务中的主要作用就是回滚事务和多版本并发机制(MVCC)。

UndoLog记录的是逻辑日志,例如我们执行一条insert的语句,UndoLog会记录一条对应的delete语句;update和delete操作也会记录对应的语句,当数据库崩溃重启或者执行回滚事务时,可以从UndoLog读取相应的数据记录进行回滚操作。

InnoDB在数据库每行数据后面添加3个字段:

  • DB_TRX_ID

用来标识本行数据最近一次修改的事务标识符我们通常称为事务id

  • DB_ROLL_PTR

指向上一个版本的行记录,能够从最新版本的行记录逐级向上

  • DB_ROW_ID

这个字段包含一个随着新数据行的插入操作而单调递增的行id,当由InnoDB存储引擎自动产生聚集索引时,聚集索引会包含这个id。

5 MVCC

5.1 快照

按照MySQL官方文档里对快照的解释来看的话应该是生成快照时会结合当前数据库状态定义出Read View,然后基于Read View和一定规则生成快照。快照只会在读已提交和可重复读隔离级别下执行查询语句时生成。

组成内容含义
m_ids当前活跃的事务编号集合
min_trx_id

最小活跃事务编号

max_trx_id定义Read View那一刻的数据库中的事务的最大id
creator_trx_id定义Read View的事务的id

在MVCC机制下判断查询类容,需要遵循以下规则:

  • 如果某版本的数据的DB_TRX_ID与Read View的creator_trx_id相等,说明这个版本的数据最后由当前事务更改,故这个版本的数据对当前事务可见;
  • 如果某版本的数据的DB_TRX_ID小于Read View的min_trx_id,说明这个版本的数据最后由已经提交的事务更改,故这个版本的数据对当前事务可见;
  • 如果某版本的数据的DB_TRX_ID大于Read View的max_trx_id,说明最后修改这个版本的数据的事务在快照生成时还未创建,故这个版本的数据对当前事务不可见;
  • 如果某版本的数据的DB_TRX_ID满足:min_trx_id <= DB_TRX_ID <= max_trx_id,且m_ids包含DB_TRX_ID,说明这个版本的数据最后由未提交的事务更改,故这个版本的数据对当前事务不可见;
  • 如果某版本的数据的DB_TRX_ID满足:min_trx_id <= DB_TRX_ID <= max_trx_id,但m_ids不包含DB_TRX_ID,说明这个版本的数据最后由已经提交的事务更改,故这个版本的数据对当前事务可见。

5.2 读已提交

在读已提交事务隔离级别下,每次查询都会生成一个ReadView

  • ReadView1

我们发现ReadView1不满足上面任何规则,因此此时结果就是“张石”。 

  • ReadView2

第1-3条规则ReadView2都不符合,可以发现目前的min_trx_id=2,max_trx_id=4,m_ids={2,3},根据规则5我们往回寻找,DB_TRX_ID=1的时候符合我们的要求,如此我们能知道ReadView2的查询结果为“张三石”。

由此可知在读已提交事务隔离级别下,无法避免不可重复读。

5.3 可重复读

在可重复读隔离级别下,只会在第一次执行查询语句时生成ReadView

 

因此我们发现两次查询,第二次无论时机如何,他们的结果都会根据第一次ReadView进行判断,结果就是“张石”。

由此可见可重复读隔离级别下可以避免不可重复读。

5.3.1 可重复读能避免幻读?

可重复读隔离级别可以一定程度上避免幻读,但是并不一定避免幻读。

从图中可知由于可重复读隔离级别的特性,第二次查询依然依然会复用第一个ReadView,因为没有符合规则的DB_TRX_ID,因此结果就是“张石”。

 但是如果出现当前读时,可重复读隔离级别将无法避免幻读。

由图可知DB_TRX_ID=2事务,在中间执行了一次update操作因此产生了当前读,从而改变了ReadView最终使两次查询结果不一样形成幻读。

 书籍:深入理解分布式事务:原理与实战

参考:MySQL-事务隔离级别与MVCC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值