问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

一文讲清MySQL中的三大日志

创作时间:
作者:
@小白创作中心

一文讲清MySQL中的三大日志

引用
CSDN
1.
https://blog.csdn.net/qq_70186387/article/details/140641179

MySQL中的三大日志(undo log、redo log、binlog)是数据库系统中非常重要的组成部分,它们各自承担着不同的职责,共同保证了数据库的事务处理能力、数据恢复能力和数据一致性。本文将详细讲解这三种日志的功能、实现原理以及它们之间的关系。

三大日志

  • undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC
  • redo log(重做日志):是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复
  • binlog (归档日志):是 Server 层生成的日志,主要用于数据备份和主从复制

undo log(存储引擎层)

undo log 是一种用于撤销回退的日志。在事务没提交之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时,可以利用 undo log 来进行回滚,以此来保障事务的原子性。

另外,undo log 还有一个作用,通过 Read View + undo log 实现 MVCC(多版本并发控制)。对于「读已提交」和「可重复读」隔离级别的事务来说,它们的快照读(普通 select 语句)是通过 Read View + undo log 来实现的,它们的区别在于创建 Read View 的时机不同:

  • 读已提交在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。

  • 可重复读在启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。

undo log持久化:undo log 和数据页的刷盘策略是一样的,都需要通过 redo log 保证持久化。buffer pool 中有 undo 页,对 undo 页的修改也都会记录到 redo log。redo log 进行刷盘时,将数据页和 undo 页持久化到磁盘上。

redo log(存储引擎层)

redo log是InnoDB存储引擎独有的,每条 redo 记录记录了某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新

  • 开启事务后,InnoDB 层更新记录前,首先要记录相应的 undo log,记录下更新前的值,然后InnoDB 引擎就会更新内存(同时标记为脏页),然后将本次对这个页的修改以redo log的形式记录下来,事务提交时,先将redo log持久化到磁盘上这个时候更新就算完成了。后续,InnoDB 引擎才会在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里。

  • redo log除了让MySQL拥有了崩溃恢复能力,保证了数据的持久性与完整性。另外,写入 redo log的方式使用了追加操作, 所以是顺序写,而写入数据时需要先找到写入位置,然后才写到磁盘,所以是随机写。顺序写比随机写高效的多,因此 redo log将写操作从「随机写」变成了「顺序写」,提升 MySQL 写入磁盘的性能。

三种刷盘策略:

  1. 每次事务提交时只写入到redo log buffer。这种方式性能最高,但是也最不安全,因为如果 MySQL 挂了或宕机了,可能会丢失最近1 秒内的事务。

  2. 每次事务提交时都将进行刷盘操作。这种方式性能最低,但是也最安全,因为只要事务提交成功,redo log 记录就一定在磁盘里,不会有任何数据丢失。

  3. 每次事务提交时都把 redo log 内容写入系统缓存中(page cache)。这种方式的性能和安全性都介于前两者中间。

日志文件组

硬盘上存储的
redo log
日志文件不只一个
,而是以一个日志文件组的形式出现的,每个的
redo
日志文件大小都是一样的。它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写。

如果redo log 文件满了,这时 MySQL 不能再执行新的更新操作,也就是说 MySQL 会被阻塞(因此所以针对并发量大的系统,适当设置 redo log 的文件大小非常重要),此时会停下来将 Buffer Pool 中的脏页刷新到磁盘中,然后将相应的 redo log 记录进行擦除,等擦除完旧记录腾出了空间,checkpoint 就会往后移动(图中顺时针),然后 MySQL 恢复正常运行,继续执行新的更新操作。

bin log(server层)

bin log是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用;依靠
binlog
同步数据,可以实现数据备份、主备、主主、主从一致,保证数据一致性

三种记录格式:

  • statement记录SQL语句原文。但statement有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;

  • row记录行数据最终被修改成什么样了,不会出现 statement下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,使 binlog 文件过大。

  • mixed:前两者的混合。

写入方式:

  • binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志

  • redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志

由于redo log 文件是循环写,是会边写边擦除日志的,只记录未被刷入到磁盘的数据日志。而bin log 文件保存的是全量的日志,保存了所有数据变更的情况,所以整个数据库上的数据不可以使用 redo log 文件恢复,只能使用 binlog 文件恢复

刷盘策略:

write,是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快 。fsync,将数据持久化到磁盘的操作

write

fsync
的时机,可以由参数
sync_binlog
控制,默认是
1

  1. sync_binlog
    =0,只写入到系统缓存中,由系统自行判断什么时候执行
    fsync
    持久化到磁盘上。

  2. sync_binlog
    =1,每次提交事务都执行
    fsync
    持久化磁盘上。

  3. sync_binlog
    =N,将日志记录写入到系统缓存,累积
    N
    个事务后进行
    fsync

两阶段提交:将
redo log
的写入拆成了两个步骤
prepare

commit

  • prepare 阶段:将更新记录写入到 redo log,同时将 redo log 对应的事务状态设置为 prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用);

  • commit 阶段:提交事务后,将更新记录写入到 bin log,然后将 binlog 持久化到磁盘(sync_binlog = 1 的作用),最后将 redo log 状态设置为 commit。

发生异常判断是否回滚:

不管是时刻 A(redo log 已经写入磁盘, bin log 还没写入磁盘),还是时刻 B (redo log 和 bin log 都已经写入磁盘,还没写入 commit 标识)崩溃,此时的 redo log 都处于 prepare 状态

在 MySQL 重启后会按顺序扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就拿着 redo log 中的 XID 去 bin log 查看是否存在此 XID:

  • 如果 binlog 中没有当前内部 XA 事务的 XID,说明 redo log 完成刷盘,但是 bin log 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。

  • 如果 bin log 中有当前内部 XA 事务的 XID,说明 redo log 和 bin log 都已经完成了刷盘,则提交事务。对应时刻 B 崩溃恢复的情况。

两阶段提交的问题:

两阶段提交虽然保证了两个日志文件的数据一致性,但是性能很差,主要有两个方面的影响:

  • 磁盘 I/O 次数高:对于“双1”配置,每个事务提交都会进行两次 fsync(刷盘),一次是 redo log 刷盘,另一次是 binlog 刷盘。

  • 锁竞争激烈:两阶段提交虽然能够保证「单事务」两个日志的内容一致,但在「多事务」的情况下,却不能保证两者的提交顺序一致,因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志的提交顺序一致。

MySQL 引入了 binlog 组提交(group commit)机制,当有多个事务提交的时候,会将多个 binlog 刷盘操作合并成一个,从而减少磁盘 I/O 的次数,如果说 10 个事务依次排队刷盘的时间成本是 10,那么将这 10 个事务一次性一起刷盘的时间成本则近似于 1。同时可以控制redo log 和bin log的刷盘策略来减少磁盘I/O。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号