MySQL 日志及数据备份恢复

MySQL 日志及数据备份恢复

MySQL三种日志详解

1、日志

日志是 MySQL 数据库的重要组成部分,记录数据库运行时期的各种状态信息。MySQL日志主要包括错误日志、查询日志、 慢查询日志、事务日志、二进制日志几大类。作为开发,我们重点需要关注二进制日志(binlog)和事务日志(redologundolog

  • 简单对比
日志 物理日志 逻辑日志 定义 作用
redo log InnoDB存储引擎层的日志 N 重做日志 事务的原子性和持久性
undo log N Y 回滚日志 事务一致性,事务的回滚和实现mvcc
binlog N MySQL Server层记录的日志 归档日志 主从数据同步,数据恢复
  • 逻辑日志:可以简单理解为记录的就是sql语句 。
  • 物理日志mysql 数据最终是保存在数据页中的,物理日志记录的就是数据页变更

2、redo log

redolog包括两部分,一个是内存中的日志缓冲(redo log buffer),另一种是磁盘上的日志文件(redo log file)。MySQL每次执行一条 DML 语句,现将记录写入到 redo log bufer,然后再后续的某个合适的时间再一次性将多个操作记录写入到 redo log file 中。这种先写日志再写磁盘的技术就是 WAl(Write-Ahead Logging)技术。

redo log 是 InnoDB 存储引擎层的日志文件,也叫做重做日志文件,用于记录事务操作的变化,记录的是数据修改后的值,不管事务是否提交都会被记录下来。在实例和介质失败时,redo log 文件就能排上用场,例如数据库断电,InnoDB 存储引擎会使用redo log 恢复到断电前的时刻,以此来保证数据的完整性。另外数据库也有了 crash-safe 能力。

redo log 的日志大小是固定的,记录满了以后需要从头循环重新写。比如可以配置一组4个文件,每个文件大小是 1GB,那么 redo log 中可以记录 4GB 的操作,InnoDB 会从第一个文件开始写入,直到第四个文件写满了,又回到第一个文件开头循环写,如下图。

image-20211207162002931

write pos是记录当前的位置,一边写一边往后移,写到第四个文件的末尾后就回到第一个文件开头。check point是当前要擦除的位置,也是往后推移并且循环的,擦除记录之前要把记录更新到数据文件中。write poscheck point之间的就是 redo log上还空着的部分,也是可以用来记录新的操作的部分。如果 write pos追上了 check point,表示 redo log 满了,这个时候不能再执行新的更新,得停下来先擦掉一些记录,把 check point 往前推进一下。

  • redo log 参数说明及刷盘机制

image-20211207163010462

2、undolog

数据库事务四大特性中有一个是原子性,具体来说就是 原子性是指对数据库的一系列操作,要么全部成功,要么全部失败,不可能出现部分成功的情况。

实际上,原子性底层就是通过 undo log 来实现的。 undo log 主要记录了数据的逻辑变化,比如一条 insert 语句,对应一条 delete 的 undo log。对于每个 update 语句,对应一条相反的 updateundo log,这样在发生错误的时候,就能回滚到事务之前的数据状态。

此外,保存了事务发生之前的数据的一个版本,还有另外的作用:

  • 可以用于回滚
  • 同时可以提供多版本并发控制下的读(MVCC),非锁定读
  • 事务开始前,将当前事务版本生成 undo log,undo log 也会生成 redo log 来保证 undo log 的可靠性
  • 当事务提交以后,undo log 并不能马上被删除,而是放入待清理的链表中,由 purge 线程判断是否还有其他事务在使用 undo 表的上一个事务之前的版本信息,从而决定是否可以清理 undo log 的日志空间

4、binlog

binlog二进制日志是 server 层的日志,无论 MySQL使用什么类型的引擎,都会存在这种日志。日志的主要作用是主从复制和时间点恢复。另外需要注意,binglog 并不记录查询语句。

  • 主从复制:在 master 端开启 binlog,然后将 binlog 发送到各个 slave 端,slave 端重放 binlog,从而达到主从数据一致
  • 数据恢复:通过使用 mysqlbinlog 工具来恢复数据

binlog 是属于 MySQL Server层面,又称为归档日志,属于逻辑日志,是以二进制的形式存在。记录的是操作语句的原始逻辑,仅仅依靠 binlog 是没有 crash-safe 能力的。binlog 不会想 redolog 那样擦掉之前的记录然后循环写,而是一直记录(超过有效期才会被清理),如果超过单日志的最大值(默认为 1G,可通过变量 max_binlog_size设置),则会新起一个文件继续记录。但是由于日志可能是基于事务来记录的,而事务是绝对不可能也不应该跨文件记录,所以如果正好 binlog 日志达到最大值但是事务还没有提交则不会切换新的文件记录,而是继续增大日志。所以 max_binlog_size 指定的值和实际的 binlog 日志大小不一定相等。

binlog 可以用于主从复制,从库利用主库的 binlog 进行重播,实现主从同步。用于数据库的基于时间点,位点等的还原操作。binlog 的模式有三种:Statement,Row,Mixed。在 MySQL5.7.7 之前,默认的格式是 Statement,之后默认是 Row。日志格式通过 binlog-format 指定。

image-20211207164649609

数据恢复

  • 恢复流程
  1. 读取 redolog 记录
  2. 从 check point开始,对日志进行重放
  3. 检查 redo log中那些事务是完整的并且处于 prepare 状态
  4. 根据 XID(事务 ID) 对照 binlog 的事务,并检查事务是否完整
  5. 事务完整,重新设置redo log 的 commit 标识
  6. 事务不完整则根据 XID 找到 undo log 进行事务回滚

下面我们根据事务提交流程,在不同的阶段时刻,看看MySQL突然奔溃后,按照上述流程是如何恢复数据的。

  1. 时刻A(刚在内存中更改完数据页,还没有开始写redo log的时候奔溃):

    因为内存中的脏页还没刷盘,也没有写redo log和binlog,即这个事务还没有开始提交,所以奔溃恢复跟该事务没有关系;

  2. 时刻B(正在写redo log或者已经写完redo log并且落盘后,处于prepare状态,还没有开始写binlog的时候奔溃):

    恢复后会判断redo log的事务是不是完整的,如果不是则根据undo log回滚;如果是完整的并且是prepare状态,则进一步判断对应的事务binlog是不是完整的,如果不完整则一样根据undo log进行回滚;

  3. 时刻C(正在写binlog或者已经写完binlog并且落盘了,还没有开始commit redo log的时候奔溃):

    恢复后会跟时刻B一样,先检查redo log中是完整并且处于prepare状态的事务,然后判断对应的事务binlog是不是完整的,如果不完整则一样根据undo log回滚,完整则重新commit redo log;

  4. 时刻D(正在commit redo log或者事务已经提交完的时候,还没有反馈成功给客户端的时候奔溃):

    恢复后跟时刻C基本一样,都会对照redo log和binlog的事务完整性,来确认是回滚还是重新提交。

总结

redo log 用来保证 crash-safebinlog 用来保证可以将数据库状态恢复到任一时刻,undo log 是用来保证事务需要回滚时数据状态的回滚和 MVCC 时,记录各版本数据信息。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!