Mysql log 杂知识

为什么需要undo log?

复制代码
1.事务回滚需要历史记录
2.MVCC机制的底层

BufferPool中的数据页和索引页什么区别?数据不也是以B+树索引的形式存储的么

对于一级索引树:数据页是真实记录,也就是B+树的叶子节点,索引页存的是一级索引树的非叶子节点用于索引

对于二级索引树:二级索引树不存真实记录,因此所有节点都在索引页中

除了在BufferPool中,在InnoDB持久化硬盘中,物理结构也分数据页索引页

什么是BufferPool中的脏页

写入硬盘非常慢,因此写入的时候先在内存中写入脏页("脏"的意思是与硬盘有不一致),后续异步写入硬盘。BufferPool中的数据页和UNDO页都先作为脏页后续被刷盘。

什么是undo页

UNDOLOG本质就是一条条记录,一个实际记录对应多个历史记录(UNDOLOG),因此UNDOLOG也作为页存储,存储在UNDO页

什么是RedoLog

在写入BufferPool的脏页(包括数据页和UNDO页)前先向RedoLog备份要对该页的操作,作为保险单提供断电恢复功能。

同时为了能断电恢复,每次写RedoLog并commit后都要把内存中的RedoLog立马持久化到硬盘

RedoLog有什么用

断电恢复

RedoLog和脏页都存储了对数据库的操作,后续写入硬盘用的谁

RedoLog只作为备份使用,存储的不是完整信息,只存储变化信息,且在硬盘中,因此直接把脏页存入硬盘对应页

RedoLog和UndoLog啥区别

UndoLog存储每个操作前的历史记录,也就是插入更新修改前的状态,方便回滚

RedoLog存储每个操作后的记录,作为预写日志WAL,断电后只需要重跑一遍RedoLog就能恢复,方便断电恢复。同时操作Undo页时也要在Redolog记录。


插入修改删除一条记录并commit的实际流程

1. 准备阶段:加载与锁定
  • 动作 :InnoDB 先看该记录所在的 数据页 和需要的 Undo 页 是否在 Buffer Pool 里。
  • 细节 :如果不在,先从磁盘 .ibd 文件和 Undo 空间里加载页到内存。同时对该行记录加锁。
2. 写入 Undo Log(后悔药):修改 Undo 页
  • 动作 :在 Buffer Pool 的 Undo 页 中插入一条逻辑记录(如:刚才删了张三,这里记下张三的所有原数据)。
  • 状态 :此时,Undo 页变成了脏页 。同时,这个修改 Undo 页的动作会产生一份 Redo Log(记录了 Undo 页的物理变化)。
3. 修改数据页(改正文):插入脏页
  • 动作 :在 Buffer Pool 的 数据页 中执行真正的增删改。
  • 状态 :此时,数据页变成了脏页 。同时,这个修改数据页的动作也会产生一份 Redo Log(记录了数据页的物理变化)。
  • 注意:对数据页增删改,可能也要动索引页,索引页也可能成为脏页
4. 记录 Redo Log Buffer(预热)
  • 动作 :将上述产生的两部分 Redo Log(针对 Undo 页的和针对数据页的)全部写入内存中的 Redo Log Buffer
  • 注意:此时所有操作都在内存,硬盘还没动。
5. 执行 Commit(生死关头:WAL 持久化)
  • 动作 :当你点下 commit 时,InnoDB 强制将 Redo Log Buffer 中的内容刷新到磁盘的 Redo Log 文件(ib_logfile) 中。
  • 意义 :这就是 WAL(预写日志)。只要日志进了硬盘,哪怕下一秒停电,数据也丢不了。
6. 响应用户(成功反馈)
  • 动作:Redo Log 刷盘完成后,MySQL 立刻给客户端返回"Query OK"或"事务提交成功"。
  • 现状 :此时,磁盘里的 .ibd 数据文件和 Undo 文件依然是旧的!最新的数据只存在于:内存脏页 + 磁盘 Redo Log。
7. 异步刷脏(落叶归根:Checkpoint)
  • 动作 :由后台线程(Page Cleaner)根据系统的繁忙程度,异步地将 Buffer Pool 里的 数据脏页Undo 脏页 真正写入磁盘的 .ibd 文件和 Undo 空间。
  • 清理:等到脏页安全落盘,Redo Log 里对应的位置就可以标记为"已失效",准备被后续的日志覆盖。

redolog什么时候刷盘

  • MySQL 正常关闭时;
  • 当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,会触发落盘;
  • InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。
  • 每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘。

binlog是什么

是在Server层实现的日志,记录了所有修改数据的逻辑sql。包含3种模式:STATEMENT,ROW,MIXED

binlog和canal什么联系

canal伪装从库监控binlog,之后将主库的记录修改信息发送给redis进行修改

binlog和redolog很像,什么区别

binlog存逻辑sql,redolog存物理变动;binlog在server层,redolog在innodb存储层;redolog循环写大小固定会覆盖,binlog追加写,写满一个开下一个永久保存;redolog主要用于崩溃恢复,binlog用于主从复制数据恢复

Mysql集群主从库功能划分

主库用来写;从库用来读

Mysql集群主库变动如何复制到从库

主库开启logdump线程,每次更新将binlog发给从库的IO线程,从库用来更新

Mysql集群从库是不是越多越好

不是。因为从库数量增加,从库连接上来的 I/O 线程也比较多,主库也要创建同样多的 log dump 线程来处理复制的请求,对主库资源消耗比较高,同时还受限于主库的网络带宽

binlog怎么刷盘

1.write:指把日志写入到文件系统的 page cache(操作系统的缓存),并没有真正落盘。这一步速度极快。

2.fsync:指将数据从 page cache 强制刷入 磁盘文件。这才叫真正的持久化。


二阶段提交

二阶段提交(Two-Phase Commit, 2PC) 是 MySQL 为了解决 Redo Log 和 Binlog 之间"内部一致性"问题而设计的。

简单来说,Redo Log 归 InnoDB 管,Binlog 归 Server 层管。如果不协调好,就会出现"主库有数据(Redo 有),从库没数据(Binlog 没)"或者反过来的灵异事件。

1. 为什么需要二阶段提交?

假设我们没有二阶段提交,而是简单地先写 A 日志再写 B 日志:

  • 先写 Redo,再写 Binlog:Redo 写完瞬间断电,Binlog 没写成。重启后主库靠 Redo 恢复了,但从库因为没收到 Binlog,数据就丢了。
  • 先写 Binlog,再写 Redo:Binlog 写完瞬间断电。重启后主库发现 Redo 没写,认为事务失败,不恢复;但从库收到 Binlog 并执行了。结果:从库多了数据。

为了让这两个日志同生共死,2PC 应运而生。

2. 二阶段提交的详细流程

当一个事务准备提交时,流程被拆分为两个阶段:

第一阶段:Prepare(准备阶段)
  1. 生成 Redo Log:InnoDB 将该事务的所有物理改动写入 Redo Log。
  2. 标记 Prepare :将这条 Redo Log 记录的状态设置为 PREPARE 状态。
  3. 刷盘 :将这条 PREPARE 状态的 Redo Log 真正刷入磁盘(受 innodb_flush_log_at_trx_commit 影响)。
  • 此时,Binlog 还没动。
第二阶段:Commit(提交阶段)
  1. 写入 Binlog:事务提交后,Server 层将事务的逻辑操作(SQL 或行数据)写入 Binlog 文件。
  2. 刷盘 :将 Binlog 真正刷入磁盘(受 sync_binlog 影响)。
  3. 写入 Commit 标记 :在 Redo Log 中追加一个 COMMIT 标记,表示事务在引擎层也正式完成。
  • 注意:这一步通常只需要 write 到系统缓存,不需要强制 fsync,因为即便这一步失败了,数据也能找回来。
3. 崩溃恢复(Crash Recovery)的逻辑

如果第二阶段失败了,磁盘中的redolog没有commit标记会怎么样

简单直接的结论是:不一定会回滚。MySQL 会根据 Binlog 的完整性来做最终裁决。

当 MySQL 崩溃恢复(Crash Recovery)发现一条 Redo Log 只有 PREPARE 标记而没有 COMMIT 标记时,它会进入一个逻辑判定流程

核心判定逻辑:以 Binlog 为准

MySQL 会拿着这条处于 PREPARE 状态的 Redo Log 里的 XID (事务 ID),去 Binlog 文件里查找。

  • 情况 A:Binlog 中找不到对应的 XID

    • 判定:说明在第二阶段,Binlog 还没写完(或没刷盘)系统就崩了。
    • 动作 :执行 回滚(Rollback)
    • 理由:既然 Binlog 没写,从库肯定没收到这条数据。为了保证主从一致,主库必须把之前的改动撤销。
  • 情况 B:Binlog 中能找到对应的 XID

    • 判定 :说明 Binlog 已经成功写入并落盘了,只是在最后往 Redo Log 补写 COMMIT 标记时系统崩了。
    • 动作 :执行 提交(Commit)
    • 理由:既然 Binlog 已经落盘,从库迟早会执行这个事务。如果主库因为没看到 commit 标记就回滚,会导致主从数据不一致(从库有,主库没)。
为什么这样做是安全的?

你可能会担心:Redo Log 里没记 commit,那内存里的数据页还是脏的吗?

  1. 恢复过程:在恢复时,MySQL 发现 Binlog 有记录,会再次触发引擎层的提交逻辑。
  2. 补写标记 :它会把这个事务在内存中重新变为"提交"状态,并补齐磁盘上 Redo Log 的 COMMIT 标记。
  3. 最终一致:这样,主库的数据状态就和 Binlog 记录的逻辑状态完全对齐了。

redolog会定时持久化,binlog只在事务提交时才会持久化么

在一个事务中会不会存在多个redolog对应一个binlog的情况

会。持久化时机不一样,定时持久化的redolog可能会有多个但每个事务只有一个binlog

那binlog持久化后,是不是要对多个redolog打上commit标记

直观上确实容易这么想,但是实际上只需要一个标记。因为 Redo Log 是按照 LSN(日志序列号) 顺序排列的。MySQL 只需要在日志流里看到某个 XID 的 COMMIT 记录,就代表在这个 LSN 之前,该事务的所有 PREPARE 记录都已经安全躺在硬盘里了。


组提交

两阶段提交的问题

1.redolog和binlog每次事务提交都要至少两次刷盘

2.早期mysql为防止出现"事务A的redolog刷盘 + 事务B的binlog刷盘"的情况(主库只有事务A的记录,从库只有事务B的记录),将"redolog刷盘+binlog刷盘"加锁,限制了高并发。这被称为顺序一致性,顺序一致性的关键是"事务A的redolog先刷盘,那么事务A的binlog也应该先刷盘"。

组提交详细的联动流程

组提交的逻辑就是:与其让 100 个事务排队去撞 100 次磁盘,不如让它们在门口等一下,由一个"领头人"带着这 100 个事务的日志,一次性撞一次磁盘。

  1. Flush Stage (写入缓冲区)
    • 多个事务进入队列,Leader 把这组事务的 Binlog 写入 OS Cache。
    • 此时 Redo Log 还在内存中,状态是 Prepare。
  2. Sync Stage (磁盘同步)
    • 核心动作 :Leader 发现有一组事务要提交,它会先触发 InnoDB 的 Redo Log 刷盘(把所有 LSN 小于当前最大值的日志全部 fsync)。
    • 接着,Leader 再执行自己的 fsync(binlog)
    • 结果:这一组事务的 Redo 和 Binlog 都在这个阶段完成了物理落盘。
  3. Commit Stage (引擎提交)
    • 既然大家都落地了,Leader 告诉 InnoDB 可以批量把这些事务标记为 COMMIT 了。
为什么要这么设计?(一箭三雕)

这种设计实现了三个目标:

  • 保证一致性:顺序被队列锁死,落盘被 Leader 统一。
  • 消灭全局锁 :不再需要 prepare_commit_mutex 这种大锁,因为队列本身就是有序的。
  • 减少 fsync 次数:Redo Log 也不再需要每个事务刷一次,而是跟着 Binlog 的节奏"组团"刷盘。

相关推荐
大榕树信息科技1 小时前
动环监控系统提升机房管理的智能化与人性化体验
数据库·人工智能·信息可视化·数据中心·动环监控系统
吾诺2 小时前
Java进阶,时间与日期,包装类,正则表达式
java·mysql·正则表达式
码哥字节2 小时前
Redis 8.0~8.4 重要更新,新特性很强!
数据库·redis·缓存
未来龙皇小蓝2 小时前
【MySQL-索引调优】05:索引相关概念
数据库·mysql·性能优化
码农阿豪2 小时前
MySQL 动态分区管理:自动化与优化实践
数据库·mysql·自动化
givemeacar2 小时前
redis 使用
数据库·redis·缓存
qiuyuyiyang2 小时前
MySQL:drop、delete与truncate区别
数据库·mysql
cga19472 小时前
MySQL:数据查询-limit
数据库·mysql
阿乐艾官2 小时前
【HBase列式存储数据库】
android·数据库·hbase