一、日志类型
- 逻辑日志binlog :存储了逻辑SQL修改语句
- 物理日志redolog:存储了数据被修改的值
二、binlog
binlog 是 MySQL 的逻辑日志,也叫归档日志、二进制日志,由 MySQL Server 来记录。 用于记录用户对数据库操作的SQL语句(除了查询语句)信息,以二进制的形式保存在磁盘中。
binlog 是通过追加的方式写入的,可通过配置参数 max_binlog_size 设置每个 binlog 文件的大小,当文件大小大于给定值后,日志会发生滚动,之后的日志记录到新的文件上,不会覆盖以前的记录。
三、redolog
redolog 是 MySQL 的物理日志,也叫重做日志,记录存储引擎 InnoDB 的事务日志,储存了数据更新后的值。
MySQL 每执行一条 SQL 更新语句,不是每次数据更改都立刻写到磁盘,而是先将记录写到 redo log 里面,并更新内存(这时内存与磁盘的数据不一致,将这种有差异的数据称为脏页),一段时间后,再一次性将多个操作记录写到到磁盘上,这样可以减少磁盘 io 成本,提高操作速度。先写日志,再写磁盘,这就是 MySQL 里经常说到的 WAL 技术,即 Write-Ahead Logging,又叫预写日志。MySQL 通过 WAL 技术保证事务的持久性。 当问到mysql事务如何保证持久性时?答案就可以说是MySQL的innoDB存储引擎,使用Redo log保证了事务的持久性。
InnoDB 的 redo log 大小是固定的,采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。如下图: write pos表示日志当前记录的位置,当ib_logfile_4写满后,会从ib_logfile_1从头开始记录; check point表示将日志记录的修改写进磁盘,完成数据落盘,数据落盘后check point会将日志上的相关记录擦除掉, 即write pos->check point之间的部分是redo log空着的部分,用于记录新的记录,check point->write pos之间是redo log待落盘的数据修改记录。当write pos追上check point时,得先停下记录,先推动check point向前移动,空出位置记录新的日志。
四、crash-safe
有了 redo log,当数据库发生宕机重启后,可通过 redo log 将未落盘的数据(check point之后的数据)恢复,保证已经提交的事务记录不会丢失,这种能力称为crash-safe。 有了 redo log,为什么还需要 binlog 呢?先来看看 binlog 和redo log 的区别:
| redo log | binlog |
---|
文件大小 | redo log 的大小是固定的。 | binlog 可通过配置参数max_binlog_size 设置每个 binlog 文件的大小。 | 实现方式 | redo log 是 InnoDB 引擎层实现的,并不是所有引擎都有。 | binlog是 Server 层实现的,所有引擎都可以使用 binlog 日志。 | 记录方式 | redo log 采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。日志上的记录修改落盘后,日志会被覆盖掉,无法用于数据回滚/数据恢复等操作。 | binlog 通过追加的方式记录,当文件大小大于给定值后,日志会发生滚动,之后的日志记录到新的文件上,不会覆盖以前的记录。 |
由 binlog 和 redo log 的区别可知:binlog 日志只用于归档,只依靠 binlog 是没有 crash-safe 能力的。为什么 binlog 没有 crash-safe 能力,当数据库 crash 后,想要恢复未刷盘但已经写入 redo log 和 binlog 的数据到内存时,binlog 是无法恢复的。虽然 binlog 拥有全量的日志,但没有一个标志让 innoDB 判断哪些数据已经刷盘,哪些数据还没有。但 redo log 不一样,只要刷入磁盘的数据,都会从 redo log 中抹掉,数据库重启后,直接把 redo log 中的数据都恢复至内存就可以了。这就是为什么 redo log 具有 crash-safe 的能力,而 binlog 不具备。但只有 redo log 也不行,因为 redo log 是InnoDB 特有的,换了一个储存引擎就没有redo log了,且日志上的记录落盘后会被覆盖掉。因此需要 binlog 和 redo log 二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。
补充:为什么 redo log 具有 crash-safe 的能力,是 binlog 无法替代的?
五、两阶段提交
当执行一条 SQL 更新语句时,过程如下: 1. 执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一 行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然 后再返回。 2. 执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行 数据,再调用引擎接口写入这行新数据。 3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处 于prepare状态。然后告知执行器执行完成了,随时可以提交事务。 4.执行器生成这个操作的binlog,并把binlog写入磁盘。 5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更 新完成
可以看到,在“两阶段提交”阶段,将 redo log 的写入分成了两步:prepare 和 commit。在 redo log 状态为 prepare 时记录 binlog 可以保证两个日志的记录一致。
如果数据库误操作, 如何执行数据恢复?
在回答这个问题之前先了解一个概率:LSN LSN 是 日志序列号, 为 log sequence number 的缩写,主要用于发生 crash 时对数据进行 recovery。LSN是一个一直递增的整型数字,表示事务写入到日志的字节总量。 LSN 不仅只存在于重做日志中,在每个数据页头部也会有对应的 LSN 号,该 LSN 记录当前页最后一次修改的 LSN 号,用于在 recovery 时对比重做日志 LSN 号决定是否对该页进行恢复数据。 前面说的check point也是由 LSN 号记录的,LSN 号串联起一个事务开始到恢复的过程。
DB宕机后重启,InnoDB 会首先去查看数据页中的LSN的数值。这个值代表数据页被刷新回磁盘的 LSN 的大小。然后再去查看 redo log 的 LSN 的大小。 如果数据页中的 LSN 值大说明数据页领先于 redo log 刷新回磁盘,不需要进行恢复。反之需要从redo log中恢复数据。
如果将 innodb_flush_log_at_trx_commit 和 sync_binlog 参数设置成 1,前者表示每次事务的 redo log 都直接持久化到磁盘,后者表示每次事务的 binlog 都直接持久化到磁盘,可以双重保证 MySQL 异常重启之后的数据不会丢失。
|