mysql中的这些日志,你都知道吗 2?

上一篇文章,我们介绍了binlog和redo log这两种日志,对这两种日志不熟悉的老铁可以看下"mysql中的这些日志,你都知道吗",在上篇文章的末尾,作者还留了一个问题:binlog 和 redo log两个相互独立的日志模块,如何保持一致性?

两阶段提交

上一篇文章我们说过,binlog是server层面的日志,redolog是存储引擎(Innodb)的日志,他们是两个独立的日志模块,但是他们的一致性对整个mysql的数据正确性是十分重要,我们举例来说明一下。当我们执行如下sql语句时:

update tablex set c=2 where id= 2;

也就是将表 tablex 中,id=2数据行中的字段c设置为2,假设该字段原值是0。

如果在执行上述语句的过程中,写完第一个日志后,第二个日志写入失败,会出现什么情况呢?

1.先写redo log,后写binlog

假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

2.先写bin log,后写 redo log

如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 2”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 2,与原库的值不同。

mysql在两个日志一致性问题上的解决方案,采用了两阶段提交的方式。具体如下图:

在这里插入图片描述

在写redo log时,将写的过程分为两个阶段:preparedcommit

具体写入流程如下:先写redo log,并将写入状态设置为prepared,然后写入binlog,最后将redo log状态设置为commit,redo log生效。

在上述日志写入流程中,会存在三处可能写失败的地方:

1.写prepared状态的redo log失败。

2.写binlog失败。

3.将redo log状态更新为 commit失败。

下面我们来一一分析一下,以上三种情况,是否会导致数据不一致。

1.对于第一种情况,写prepared状态的redo log失败,此时后面流程就会终止,两个日志都不会写入,数据是一致的。

2.对于第二种情况,prepared状态redo log写入成功,binlog写入失败。此时当mysql异常重启后,会检查redo log日志,发现这条只有 prepared状态的redo log日志后,会使用该条日志的 Xid(一次事务提交后,针对这个事务的binlog日志和redo log会使用相同的Xid来标记,根据Xid来标识binlog 和 redo log之间的对应关系)查找binlog,由于binlog写入失败,根据Xid无法查找binlog,此时会将写入的该条redo log进行回滚,实现两个日志都没有写入成功的效果,此时数据也是一致的。

3.对于第三种情况,redo log设置为commit状态时失败,mysql重启后,会像步骤2一样查找binlog,此时查找操作可以找到对应的binlog,那么mysql会将该条redo log自动设置为commit状态,默认为写入成功,实现两个日志都写入成功的效果,此时数据也是一致的。

undo log(回滚日志)

undo log又叫做回滚日志,是mysql实现mvcc(多版本并发控制)中的重要日志。我们知道在mvcc中为了实现可重复读,一行数据会保留多个版本,并且使用事务id作为数据的版本号,在一个事务中多次查询数据时,只会查找某行数据指定版本的内容,即使数据被更新了,也可以实现重复读的能力。关于mvcc的更多内容,可以参考"mysql中事务id,有啥用?"。

而mysql在实现对一行数据存储多个版本的时候,并不是对一行数据存储多份,而是采用 undo log日志的方式实现的,具体可以参考下图:

在这里插入图片描述

图中虚线框中,同一行数据有4个版本,当前最新的数据是V4,字段k的值是4,它的版本号事务id是25。为了便于说明,上图中的一行数据存在4个版本V1,V2,V3和V4,好像该行数据存在4份,其实V1,V2和V3并不是物理存在,而是在每次查询时,根据当前版本V4和redo log计算出来的,比如,如果现在需要查询版本号为10的数据时,就通过V4的数据内容依次执行U3,U2,U1进行计算。这里的U3,U2,U1就是undo log日志了。

到这里,细心的读者可能会提出这样一个问题,如果要查询一个版本比较老的数据时,性能是不是会比较差?答案是肯定,因为需要使用undo log进行多次回滚计算,其实这也是mysql"突然"变慢的一个原因,关于mysql突然变慢的其他情况,有兴趣的老铁可以参考"mysql查询一行数据也很慢?"。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值