【深入浅出Mysql】日志

278 阅读5分钟

Mysql主要有两种日志,Redo Log和BinLog。

BinLog

  1. binlog 用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。由MySQL 的 Server 层实现的,所有引擎都可以使用

  2. BinLog是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。

  3. binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

  4. 使用场景:主从复制和数据恢复

  5. BinLog有三种模式:Row Level 行模式,Statement Level(默认),Mixed 自动模式。

  • Row Level 模式会记录每一行数据被修改的形式,在slave端再对相同的数据进行修改。这种模式日志记录最为完整,但是日志量大。

  • Statement Level 记录每一条修改数据的sql,slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行。这种模式能够减少减少bin-log日志量,但是在修改数据的时候使用了某些定的函数或者功能的时候会出现问题。比如:sleep()函数在有些版本中就不能直接复制,在存储过程中使用了last_insert_id()函数,可能会使slave和master上得到不一致的id等等。由于rowlevel是基于每一行来记录的变化,所以不会出现类似的问题

  • Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志格式,也就是在Statement和Row之间选择一种。如果sql语句确实就是update或者delete等修改数据的语句,那么还是会记录所有行的变更。

  1. 查看日志模式
查看日志模式 select @@binlog_format

Redo Log

  1. InnoDB 引擎特有
  2. 物理日志,记录的是“在某个数据页上做了什么修改”
  3. redo log 是循环写的,空间固定会用完
  4. Innodb引擎使用WAL(Write-Ahead Logging)技术来提升效率,技术的关键是先写日志再写磁盘。当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面。
  5. crash-safe:有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
  6. 为什么有binlog还要redo log:binlog不具备奔溃恢复的能力,这是因为binlog没有check point,无法确定从哪里开始重放日志。

Redo Log日志结构

redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么日志总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下图所示。 图中展示了一组 4 个文件的 redo log 日志,checkpoint 是当前要擦除的位置,擦除记录前需要先把对应的数据落盘(更新内存页,等待刷脏页)。write pos 到 checkpoint 之间的部分可以用来记录新的操作,如果 write pos 和 checkpoint 相遇,说明 redolog 已满,这个时候数据库停止进行数据库更新语句的执行,转而进行 redo log 日志同步到磁盘中。checkpoint 到 write pos 之间的部分等待落盘(先更新内存页,然后等待刷脏页)。

日志的记录与奔溃恢复

当更新某一行数据的时候,与日志相关的操作如下

  1. 这里redolog会进行两段提交,这样设计的目的是为了保证数据库的状态和用它的日志恢复出来的库的状态一致。

  2. 假设不使用两段提交,会有一下情况:

  • 先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
  • 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
  1. 当系统奔溃时,恢复的过程如下:redo log 和 binlog 有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:
  • 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
  • 如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。