MySQL日志之Redo Log、Undo Log、Bin Log

405 阅读6分钟

MySQL 日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志 binlog(归档日志)和事务日志 redo log(重做日志)和 undo log(回滚日志)。

我们先看一张图,大概看一下这些日志的工作流程:

在存储引擎与最后紫色区域的磁盘之间应该还有系统缓存OS Buffer(有的地方也叫page cache)

Redo Log

Redo Log也叫重做日志,是InnoDB存储引擎下才有的,记录的是物理日志,也就是磁盘数据页的修改,通过它可以实现事务持久性。

比如 MySQL 实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。

为了提高性能,MySQL不会把修改内容直接写入到redo log文件里面,而是先写入到redo log buffer日志缓冲中去。如图的第四步与第五步,从图中可以看出,redo log是存储引擎的。

redo log刷盘时机

具体什么时候可以把Redo Log Buffer刷到Redo Log FIle中,可以通过innodb_flush_log_at_trx_commit参数配置决定。

 0(延迟写) 提交事务后,不会立即刷到OS Buffer中,而是等一秒后刷新到OS Buffer并调用fsync()写入Redo Log FIle,可能会丢失一秒钟的数据。 

1(实时写)【默认值】 每次提交事务,都会刷新到OS Buffer并调用fsync()写到Redo Log FIle,性能较差

2(延迟刷新) 每次提交事务只刷新到OS Buffer,一秒后再调用fsync()写入Redo Log FIle。

Undo Log

undo log也叫回滚日志,也是InnoDB存储引擎下才有的,记录的是逻辑日志,也就是SQL语句,比如:当我们执行一条insert语句时,Undo Log就记录一条相反的delete语句。通过它可以实现**:**

  • 事务回滚:可以对提前写入的数据变动进行擦除,实现事务回滚,保证事务的原子性
  • 实现 MVCC 机制:Undo Log 也用于实现 MVCC 机制(实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB 通过数据行的 DB_TRX_IDRead View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改),存储记录的多个版本的 Undo Log,形成版本链。

binlog

binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层。

不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。

开启Binlog日志有以下两个最重要的使用场景:

  • 主从复制:在主库中开启Binlog功能,这样主库就可以把Binlog传递给从库,从库拿到Binlog后实现数据恢复达到主从数据一致性。

  • 数据恢复:假如不小心删库删表了,可以通过mysqlbinlog工具来恢复数据。

写入机制

binlog的写入时机也非常简单,事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。

因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache

我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。

binlog日志刷盘流程如下

两阶段提交

redo log(重做日志)让InnoDB存储引擎拥有了崩溃恢复能力。

binlog(归档日志)保证了MySQL集群架构的数据一致性。

虽然它们都属于持久化的保证,但是侧重点不同。

在执行更新语句过程,会记录redo logbinlog两块日志,以基本的事务为单位,redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,所以redo logbinlog的写入时机不一样。

假如一个update语句,他会记录redo log和binlog。假设执行过程中写完redo log日志后,binlog日志写期间发生了异常,会出现什么情况呢?

如果在主从复制场景下,主库是最新的值,但是从库依然是旧值,因为主从复制是通过binlog进行同步的,现在binlog异常了,自然出现这个问题。为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。

原理很简单,将redo log的写入拆成了两个步骤preparecommit,这就是两阶段提交

使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务。

再看一个场景,redo log设置commit阶段发生异常,那会不会回滚事务呢?

并不会回滚事务,它会执行上图框住的逻辑,虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。

Redo Log和Binlog区别

  • Redo Log是属于InnoDB引擎功能,Binlog是属于MySQL Server自带功能,并且是以二进制文件记录。

  • Redo Log属于物理日志,记录该数据页更新状态内容,Binlog是逻辑日志,记录更新过程。

  • Redo Log日志是循环写,日志空间大小是固定,Binlog是追加写入,写完一个写下一个,不会覆盖使用

Buffer pool

MySQL 中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。

后续的查询都是先从 Buffer Pool 中找,没有命中再去硬盘加载,减少硬盘 IO 开销,提升性能。

更新表数据的时候,也是如此,发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。

看文章开头的那张图,buffer pool通过redo log和undo log两个文件来更新内容,然后会有一个I/O线程以page页为单位随机写入数据库的磁盘(第八步)。

参考文章:

MySQL三大日志(binlog、redo log和undo log)详解

Mysql buffer pool详解