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在两个日志一致性问题上的解决方案,采用了两阶段提交的方式。具体如下图:

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

在写redo log时,将写的过程分为两个阶段:prepared和commit。

具体写入流程如下:先写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日志的方式实现的,具体可以参考下图:

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

图中虚线框中,同一行数据有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查询一行数据也很慢?“。

      

原文始发于微信公众号(小李哥编程):mysql中的这些日志,你都知道吗(2)?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/95728.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!