对Mysql中redo logundo logbinlog深入理解

Posted 敲代码的小小酥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对Mysql中redo logundo logbinlog深入理解相关的知识,希望对你有一定的参考价值。

redolog与binlog

redolog是InnoDB引擎中的日志,在其他引擎中没有。binlog是在mysql服务层中的日志,所有的存储引擎都有binlog日志。

那么问题来了,既然有了binlog日志,为何又要有redo log日志呢?

因为在InnoDB引擎中,是支持事务的。事务有持久性的特点。而引入redo log,就是为了保证持久性这个特点的。那么redo log是如何保证持久性的呢?那就先看一下mysql缓冲池的机制。

缓冲池:
Mysql数据库把数据存储到了磁盘上。那么在查询数据,修改数据时,每次都从磁盘中来获取数据,性能是极差的。因此,mysql采用了缓冲池+预读取机制,来减少与磁盘的IO次数。所谓预读取,就是将命中数据相邻的数据,也都一起读取出来,形成一页。然后将这个页的数据,存储到缓冲池中。当对缓冲池中的数据进行增删改查时,就无需再次IO了,直接从内存读取即可。提升了效率。
由此带来的问题就是,我们增删改了缓冲池中的数据,缓冲池数据并不会第一时间刷盘(将缓冲池数据写入磁盘),这就造成了缓冲池中数据与磁盘数据不一致的情况出现,称之为脏页。InnoDB引擎会定时刷一次脏页到磁盘,减少IO次数。
那么当脏页还没刷盘,此时mysql宕机,是不是就造成了数据丢失呢?已经提交的事务,在磁盘是不是就没有被修改呢?是不是就不满足事务持久性原则了呢?因此,redo log应运而生。

WAL:
预写日志机制。详细原理不在这里讲述,可以参考其他资料。大概意思就是,采用顺序IO,将数据的变化,追加到一个日志文件中,然后再将这些数据采用异步或其他方式,采用随机IO,写入最终的磁盘文件中。Mysql的redo和undo就是采用了这种机制。
拿redo日志来说,事务的提交,并不是一下将数据写入到了磁盘中。而是将数据的变化,记录到了redo日志中。redo日志是顺序IO,速度很快。redo日志的格式类似于:

xx 表空间,xx 页,xx 位置,xx 值

这样,写入redo log中的数据,才算事务提交成功,没有写入redo log中的数据,都不算事务提交成功的。当脏页没有刷盘,mysql就宕机时,redo log中记录了提交事务的数据,重启mysql后,从redo log中读取脏页丢失的数据,并恢复,这样,就保证了事务的持久性。

由此可见,redo log是基于解决随机IO影响性能和事务持久性保证两个方面,设计而生的。

两阶段提交:
redo log在InnoDB中工作是按两阶段提交来提交事务的。这跟分布式事务的两阶段提交很像,只不过这是mysql内部的两阶段提交。具体流程如下:

1.服务器收到事务开始的指令,为事务生成一个全局唯一的事务 id。这个事务 id 在记录 binlog 和 redo log 时都会使用。
2.如果缓存池中没有 id=1 所在数据页的数据,从磁盘中找到对应的数据页(注意,这里是一个数据页,不是一条记录),把数据页加载到缓存。
3.修改缓存数据页中 id=1 的数据。
4.记录数据到 redo log buffer、binlog cache。根据 redo log 刷盘的策略,这个过程中 redo log buffer 可能会被刷新到磁盘。
5.服务器收到事务提交的指令。
6.刷新 redo log buffer 到磁盘,并标记该事务的状态为 prepare。此操作称为 redo log prepare。
7.刷新 binlog cache 到磁盘。
8.刷新 redo log buffer 到磁盘,并标记该事务的状态为 commit。此操作称为 redo log commit。
9.向客户端返回事务执行的结果。

可以看到,一个事务写入redo log中时,有两个状态,一个是prepare预提交状态,一个是commit提交状态。为何要设置两个状态呢?因为需要将日志写到binlog日志中,这条事务才算真正提交成功。所以,在先写入redo日志后,设置为prepare状态,等确保写入binlog日志后,再设置为commit状态。以此来保证redo log日志和undo 日志的一致性。

当mysql在第6步和第7步之间宕机时,因为redo log里是prepare状态,而binlog中也没有这条记录,所以,这个事务算提交失败的。当在第7步和第8步之间宕机时,虽然redo log为prepare状态,但是InnoDB会在binlog中查找是否有这条事务,如果有,也视为事务提交成功,执行恢复操作。如果没有,就视为事务提交失败,执行回滚操作。
由上面可知,事务提交成功与否,还是以是否写入binlog为主的。而redo log的作用,就是在mysql重启时,恢复脏页中数据而用的。

redo日志结构:

是一个环形的,可重用的结构。已经写入的事务数据就会被覆盖。这里不做详细阐述,可以参考其他资料。由其结构也可以看出,其就是为恢复数据而用的,而不是备份数据。

binlog日志:
binlog 是 MySQL 服务器层面实现的一种二进制日志,用于记录所有对数据库的更改操作(这种日志被称为逻辑日志)。比如你 update 一条记录,服务器就会记录一条对应的信息到 binlog。但在 InnoDB 中,这个 binlog 是以事务为单位刷新到磁盘的。基于 binlog 的这种特性,一般我们会将 binlog 用于以下几个方面:

数据库增量备份与恢复:在使用备份还原数据后,可以使用 binlog 中记录的内容对备份时间点(简称备份点)后的数据进行恢复。因为 binlog 会还会记录下更改操作的时间,所以 binlog 可以恢复到某一具体时间点的数据。这就为我们删库后提供了除跑路以外的第二个选项:使用 binlog 恢复数据。
主从复制:MySQL 从服务器可以通过订阅 binlog 实现对主服务器的增量复制。
审计:通过对 binlog 中的数据进行审计,判断是否存在安全问题,比如 SQL 注入。

参考文章:MySQL 的 redolog 如何保证数据不丢?其中原理你真的知道吗?

undo log

在执行修改数据之前,都会将修改数据的反向操作,写入undo log中,如:

update user set name = "李四" where id = 1; ---修改之前name=张三

执行这个sql之前,undo log会记录一条相反的update语句:

update user set name = "张三" where id = 1;

这样,当事务执行失败,即没有写入binlog时,就会执行undo log日志,恢复之前数据。

还有一个MVCC作用,这个在之前文章讲过,这里不在讲解。

以上是关于对Mysql中redo logundo logbinlog深入理解的主要内容,如果未能解决你的问题,请参考以下文章

MySQL的redo logundo logbinlog

MySQL中的日志(redo logundo logbinlog)

MySQL中的日志(redo logundo logbinlog)

MySQL系列之日志汇总:redo logundo logbinlogerrorlogslow query loggeneral logrelay log

MySQL 进阶 InnoDB引擎 -- 逻辑存储结构架构(内存结构磁盘结构后台线程)事务原理(事务基础redo logundo logMVCC多版本并发控制:版本链 ReadView)

MySQL--buffer poolredo logundo logbinlog