redo log、undo log 以及mysql mvcc 实现原理

redo log

redo log(重做日志) 是物理日志,记录的是在某个数据页上做了什么修改,他记录修改前后的值。redo log 是一种 Write-Ahead Logging,他的目的是保证事务的持久性。redo log 的组织形式是:
redo log —> redo log block —> redo log group

日志格式

  • redo log 的格式
    在这里插入图片描述
  • redo log block 的格式
    在这里插入图片描述
  • redo log group 的格式
    在这里插入图片描述

两阶段提交

redo log 的两阶段提交是为了保证 redo log 个 binlog 的一致性,两阶段提交的过程如下:
在这里插入图片描述
那么,为什么要进行两阶段提交保证redo log和binlog的一致性呢?用反证法思考这个问题

  1. 先写redo log,再写binlog:在一个更新操作提交后,如果在redo log写成功之后,没有来的及写 binlog ,此时发生了异常重启,由于redo log已经记录成功,所以在异常重启之后,mysql可以根据redo log中的呢绒恢复到异常重启之前数据更新后的结果,但是由于binlog没有记录这个更新操作,如果日后你拿着这个binlog还原一个辛苦的时候,被还原的数据库中的数据就不会有这个更新操作,此时的新库和原库的数据是不一致的
  2. 先写binlog,再写redo log:在一个更新操作提交后,如果在记录完成binlog之后,还没来得及写redo log,此时发生了异常重启。由于binlog已经记录了修改操作,所以日后在使用这个binlog还原新库的时候,被还原的库中会包含这个更新操作,但是由于redo log没有被记录,数据库在异常重启后,并不能恢复到这个更新操作之后的数据内容,此时的新库和原库的数据也是不一致的
    现在,可能会有新的疑问,两阶段提交,也有在过程中宕机的可能,如果在过程中宕机了,数据库的状态以什么为准呢?答案是以binlog为准,在每次宕机重启之后,mysql server都会对比binlog和redo log,如果binlog已经提交,那么就对比后把binlog已经提交的事务但是innodb未提交的数据在redo log中重新提交;如果binlog没有提交,在重启后内存中的binlog日志因为宕机小时,则redo log回滚掉自己未提交的log。(其实这里我自己有点不能自恰)

undo log

undo log 是逻辑日志,记录的是sql语句的反向操作sql,比如说,执行insert的时候,会生成一条响应的delete语句(举个例子)

日志格式

insert语句的日志格式如下,insert undo log是指在insert操作中产生的undo log,因为insert操作记录,只对事务本身可见,对其他事务不可见(事务隔离性的要求),所以undo log可以在在事务提交之后直接删除,不需要依赖perge操作
在这里插入图片描述

update(包括update和delete) 语句的日志格式如下,update undo log相比insert,增加了更新前被更新字段信息和各索引列的信息。这里值得注意的是,1.如果update主键,会先产生一条delete的uodo log把原来的记录标记删除,然后产生一条insert的undo log插入一条新数据;2.如果是delete语句,并不直接删除激励,会先把数据标记为已删除,也就是将记录的delete flag标记为1,记录最终的删除操作由purge完成
在这里插入图片描述

undo log和redo log的关系

在这里插入图片描述
策略1: force + no steal。已经提交的事务必须强制写入磁盘,没有提交的事务只能保留在内存里,等事务提交之后再写入磁盘,这种策略不需要redo log和undo log,仅靠数据本身就能保证持久性和原子性。但显然不可行,未提交事务不能写入磁盘还可以接受,但是每次事务提交读要写入磁盘,这需要多次I/O,性能会受影响。
策略2: no force + no stale。未提交的事务不能写入磁盘,已提交的事务也可以不用立即写入磁盘。在这个策略下只需要redo log保证数据的持久性,因为有“内存断电消失”这个天然特性。
策略3: force + steal。已提交的事务必须写入磁盘,未提交的事务也可以写入磁盘。这个时候只需要undo log回滚宕机时未提交的事务,不需要redo log。但是跟策略一样,需要多次I/O对性能影响大,不可行。
策略4: no force + steal。已提交的事务不用立即写入磁盘,未提交的事务也可以写入磁盘,这个时候需要redo log和undo log一起来保证事务的持久性和原子性。通俗点讲,无论事务是否提交,都可以立即写入磁盘,也可以不写入磁盘,写入磁盘的时机是任意的,想什么时候写就什么时候写。

这innodb目前使用的是策略4,相比策略2来说,好的地方在于提高了I/O效率,因为事务没有提交就开始写磁盘,等事务提交的时候写入磁盘的数据量会小,不然要把所有数据都积累到事务提交的时候一次性写入。

mvcc

undo log版本链

这里重新来回顾一下undo log的日志格式如下图。其中data_trx_id就是最近更新这条数据的事务id,data_roll_ptr就是指向你更新这个事务之前生成的undo log。
在这里插入图片描述
举个例子:

  1. 假设事务A(id=50)插入了一条数据,那么此时这条数据的隐藏字段以及指向的undo log如下图所示。因为这条数据是新插入的(之前没有),所以roll_pointer指向一个空的undo_log在这里插入图片描述
  2. 事务B(id=58)修改了这条数据,把值改成了B,那么此时更新之后会生成一个undo log记录之前的值,然后会让roll_pointer指向这个实际的undo log回滚日志,这个undo log就会记录你更新之前的那条数据的值。
  3. 事务C(id=60)同理

readview

readview四个主要元素
m_ids:表示在生成readview时,当前系统中活跃的(未提交的)读写事务id列表
min_trx_id:表示在生成readview时,当前系统中活跃的读写事务中最小的事务id,也就是m_ids中最小的值
max_trx_id:表示生成readview时,系统中应该分配给下一个事务的id值
creator_trx_id:表示生成该readview的事务的事务id

同样举个例子:
一开始的时候,数据库如下:
在这里插入图片描述
这个时候两个事务并发执行,事务A(id=50)事务B(id=58),事务A直接开启一个readview,readview里的m_ids就包含了事务A和事务B的两个id,50和58(表示这两个事务是活跃事务)。然后min_trx_id就是50,max_trx_id就是59,creator_trx_id就是50。事务A第一次查询这行数据时,判断当前这行数据的trx_id是否小于readiew中的min_trx_id,发现trx_id=32,是小于min_trx_id =50的,证明在事务A查询之前,这个事务早已经提交了,所以可以直接查到这条数据。事务B开始执行,将数据修改为B,trx_id设置为自己的id=58,同事roll_pointer指向了修改前生成的一个undo log,接着事务B就提交了。如下图所示
在这里插入图片描述
此时事务A再次查询,发现trx_id=58,这个id大于min_trx_id(50),小于max_trx_id(59),说明更新这条数据的事务很可能是跟自己差不多同时开始的,于是会看一下trx_id=58是否在readview的m_ids列表里。查找之后发现果然在,于是顺着undo log的版本链条往下找,就会找到trx_id=32,这个事务提交在A事务开启之前,对A事务是可见的。这就是undo log版本链(mvcc)的作用,他可以保存一个链条快照,让事务可以读到之前的值。
以上是RR的例子,只在事务开始的时候生成一个readview

读已提交如何基于ReadView机制实现的
RC级别的核心是每次发起查询都重新生成一个readView。第一次查询的时候,发现数据的trx_id在自己的min和max之间和m_ids之中,所以它是一个活跃的事务修改的,并且此时尚未提交,所以它会顺着undo log版本链查找以及提交的事务。第二次查询的时候,重新开启一个新的readview,发现数据的trx_id不在m_ids中,说明该事务已经提交,所以直接读取数据行的数据。这就是RC级别的实现原理。

可重复读如何基于ReadView实现的
可重复读的核心是只在事务开始时生成一个readView,后续再次读的时候,即使事务B提交了,但是事务A它的readView还是刚开始事务时候的,所以认为B还是活跃的,所以继续沿着undo log版本链查询,读取到始终一样。MySQL的可重复读还解决了幻读,因为新插入的数据记录的trx_id要么大于事务A的max_trx_id,要么在m_ids之中(而且非本身)所以认为插入的数据记录时不能被读取的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值