Files
examination/topics/database/mysql-acid/code_reading.json
T
wonder 515acbcc7f
Deploy Examination / deploy (push) Successful in 4s
feat: add database topic group with 105 questions
- mysql-acid (35): undo/redo log, WAL, 崩溃恢复三阶段, 两阶段提交
- mysql-transaction-isolation (35): 四隔离级别, 三异常, Read View, 快照读/当前读, next-key lock
- redis-persistence (35): RDB/AOF/混合持久化, AOF 重写, vs binlog/redo log
- 每子主题: single_choice×10 + fill_blank×10 + short_answer×10 + code_reading×5
- 全部通过 question.schema.json 校验
2026-09-07 19:17:38 +08:00

241 lines
15 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"topic": "mysql-acid",
"type": "code_reading",
"schema_version": "1.0.0",
"generated": "2026-09-07T00:00:00+08:00",
"questions": [
{
"id": "cr-001",
"type": "code_reading",
"difficulty": 3,
"tags": [
"崩溃恢复",
"redo log",
"undo log",
"两阶段提交"
],
"question": "请阅读以下 SQL 序列并思考崩溃点对 redo/undo 恢复的影响。假设数据库已开启 binlog,事务按两阶段提交协调 redo 与 binlog。",
"code": "-- 事务 T1:在多个节点同步写入插入数据,然后发生崩溃\nBEGIN;\nINSERT INTO orders(id, amount) VALUES (1001, 500);\nUPDATE orders SET amount = 900 WHERE id = 1001;\n-- ← 此处崩溃点:redo log 已完成 prepare,binlog 尚未写入完成\nCOMMIT;",
"language": "sql",
"source": null,
"related": [],
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "在上述崩溃点(redo prepare 完成、binlog 未完整写入)恢复时,该事务 T1 应如何处理?",
"options": {
"A": "直接提交,因为 redo 已写入 prepare",
"B": "回滚(不提交),因为 binlog 中没有完整的该事务记录,两阶段提交判定其未成功提交",
"C": "无条件重做该事务的全部语句",
"D": "丢弃所有日志,保持崩溃前内存状态"
},
"answer": "B",
"explanation": "两阶段提交以“binlog 是否完整生成”作为提交裁决依据:若崩溃发生在 binlog 写入前(redo 仍停在 prepare / xa 状态未 commit),则恢复时判定该事务未成功提交,不重做,反而需要回滚其操作。所以 T1 被回滚,重做的是那些 binlog 完整的已提交事务。A、C 错误。"
},
{
"index": 2,
"type": "short_answer",
"question": "恢复时,InnoDB 用哪个日志把 T1(未提交事务)的 INSERT 和 UPDATE 撤销回初始状态?请说明该日志记录的内容特征。",
"answer": "使用 undo log 回滚 T1。undo log 记录的是事务修改前的旧值(逻辑逆操作,例如把 amount 从 900 改回 500、删除 INSERT 插入的 id=1 行),回滚时按逆序执行这些逻辑操作,使数据恢复到事务开始前的一致状态。",
"explanation": "undo log 保存了修改前的旧值,用于把未提交事务的修改撤销。崩溃恢复在确认 T1 未提交后,沿 undo 链逆向执行,将 INSERT 的新行删除、把 amount 改回原值。重做已提交事务靠 redo log,而回滚未提交事务靠 undo log。",
"keywords": [
"undo log",
"回滚",
"旧值",
"逆序"
],
"scoring_rubric": "总分 4 分:答出用 undo log(1 分);说明记录旧值/逆操作的特征(2 分);说明回滚 INSERT/UPDATE 到原状(1 分)。"
}
],
"explanation": "本大题考察崩溃恢复时 redo/undo 的职责分工与两阶段提交裁决:binlog 完整则重做(redo),binlog 不完整则回滚(undo)。"
},
{
"id": "cr-002",
"type": "code_reading",
"difficulty": 3,
"tags": [
"Buffer Pool",
"脏页",
"刷盘",
"WAL",
"持久性"
],
"question": "请阅读下面描述 Buffer Pool 脏页刷盘流程的伪代码。",
"code": "-- 伪代码:处理一个批量 UPDATE 时脏页与 redo 的关系\nBUF_POOL.READ_PAGE(page_back; mem_page,P);\nmutmem.page.P: modify rows (rows 1..N0) -- 只改内存,不落盘\nredo.write(mem_page.P 的物理变更) + fsync() -- 先写日志(WAL)\nmark_page_dirty(P); -- 该页成为脏页\n-- 之后某时刻(checkpoint/后台刷脏)才做:\nflush_dirty_page(P) → 写入数据文件; clear_dirty_flag(P);",
"language": "text",
"source": null,
"related": [],
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "根据上述流程,若在“flush_dirty_page(P)”执行前数据库崩溃,重启后最多丢失多少内容?",
"options": {
"A": "因为 redo 已先行持久化,数据不会丢失,重启后通过重放 redo 恢复该页修改",
"B": "脏页 P 的所有修改全部丢失,无法恢复",
"C": "丢失部分行,具体不可预测",
"D": "整个数据库损坏需要重建"
},
"answer": "A",
"explanation": "由于 redo log 在修改内存后立即(WAL)写盘并被 fsync 持久化,即使脏页 P 还未刷盘就崩溃,重启时也能通过扫描并重放 redo log 把该页的修改恢复,所以不会丢数据。这正是“先写日志再写数据”的意义所在,因此 A 正确。"
},
{
"index": 2,
"type": "short_answer",
"question": "请解释:为什么脏页可以延迟批量刷盘而不破坏持久性?",
"answer": "因为 WAL 已经保证:任何数据页修改在刷到磁盘之前,对应的 redo log 已持续化(先写日志后写数据)。因此即使有大量脏页还未写回数据文件,崩溃时也能通过重放 redo log 重建这些页的最新状态。延迟批量刷盘(checkpoint/后台刷脏)既保持持久性,又把多次小的随机写合并成大的顺序批量 IO,显著提升写性能。",
"explanation": "持久性不依赖脏页及时刷盘,而依赖 redo log 先行持久化。批量刷盘牺牲的是脏页在内存的驻留时间,换取更高的 IO 效率,同时持久性完整保住。",
"keywords": [
"WAL",
"redo log 先落盘",
"重放恢复",
"批量刷盘",
"性能"
],
"scoring_rubric": "总分 4 分:指出 WAL/redo 先行持久化(2 分);说明崩溃可重放恢复(1 分);说明延迟批量刷盘提升 IO 性能且不破坏持久性(1 分)。"
}
],
"explanation": "本大题考察 Buffer Pool 脏页 + WAL 的组合机制:内存改页、日志先行、延迟刷盘、崩溃重放。"
},
{
"id": "cr-003",
"type": "code_reading",
"difficulty": 3,
"tags": [
"两阶段提交",
"redo log",
"binlog",
"一致性"
],
"question": "请阅读下面的两阶段提交流程描述。",
"code": "-- SQL 段:事务 T 内做一次 UPDATE\nBEGIN;\nUPDATE accounts SET bal = bal - 100 WHERE id = 1;\nCOMMIT;\n-- InnoDB 内部(binlog 与 redo)协调时序:\n 1) 写 redo( prepare 阶段) -- 暂不提交\n 2) 写 binlog(该事务的完整日志) \n 3) 写 redo( commit 标记) -- 事务正式提交",
"language": "text",
"source": null,
"related": [],
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "若崩溃恰好发生在“步骤 2 写完 binlog 之后、步骤 3 写 redo commit 之前”,重启后该事务应如何?",
"options": {
"A": "直接回滚,因为 redo 未 commit",
"B": "判定为已提交并重做(因为 binlog 已完整,xid 存在),重新标记提交,保证主从一致",
"C": "既不提交也不回滚,永久悬挂",
"D": "丢弃该 UPDATE 的数据,但保留 binlog"
},
"answer": "B",
"explanation": "这是两阶段提交设计的典型场景:只要 binlog 完整写入,就认为事务可以提交。恢复时会发现该事务 redo 处于 prepare 但 binlog 已包含此 xid,于是判定提交成立,对 redo 进行提交(commit),并重做其修改。这样主库重放后的数据与从库(从 binlog 复制)一致。A、C、D 均不符合该裁决逻辑。"
},
{
"index": 2,
"type": "short_answer",
"question": "两阶段提交如何保证崩溃后 redo log 与 binlog 反映的事务集合一致?",
"answer": "关键是以 binlog 作为一致性的“权威事务集合”。崩溃重启时扫描 redo 与 xa 事务状态:若某个事务在 binlog 中有完整记录,则判定为已提交(提交并重做);若 binlog 中无完整记录,则判定未提交并回滚。这样“已提交”的事务集合在 redo 和 binlog 中完全一致,主库重放后与从库 binlog 重放结果一致,从根本上消除两日志不一致导致的主从不一致或 PITR 偏差。",
"explanation": "两阶段提交通过在 redo 中区分 prepare/commit 阶段 + 以 binlog 为准的裁决,保证两日志在某时刻崩溃时仍能收敛到一致结果。",
"keywords": [
"以 binlog 为准",
"已提交才重做",
"未提交回滚",
"主从一致",
"xa 事务"
],
"scoring_rubric": "总分 5 分:解释以 binlog 是否完整作为提交裁决(2 分);说明完整则提交/重做(1 分);不完整则回滚(1 分);指出结果与理由主从/PPTR 一致(1 分)。"
}
],
"explanation": "本大题考察两阶段的“binlog 为准”裁决机制与崩溃后 redo/binlog 一致性保证。"
},
{
"id": "cr-004",
"type": "code_reading",
"difficulty": 3,
"tags": [
"WAL",
"写时序",
"redo log",
"持久性"
],
"question": "请阅读下面的时序描述并作答。",
"code": "-- 单事务提交时 InnoDB 的写时序(同步点)\n(1) 在 Buffer Pool 修改内存页 P(标记脏页)\n(2) 将该页的物理变更追加到 redo log buffer\n(3) 在事务提交时:将 redo buffer 写入 OS 缓存并 fsync 到磁盘\n(4) 然后根据 innodb_flush_log_at_trx_commit 取值决定何时 fsync\n(5) 数据页 P 仍留在内存,等待后续 checkpoint 刷盘",
"language": "text",
"source": null,
"related": [],
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "下列哪一条关于上述写时序的描述是正确的?",
"options": {
"A": "先写数据页 P 到磁盘,再写 redo log,此顺序是 WAL",
"B": "数据页 P 同步立即刷盘,redo log 最后才写",
"C": "先记录 redo log 并持久化(WAL),数据页 P 可延迟刷盘,从而保证崩溃可恢复",
"D": "redo log 与数据页 P 必须同时在一个事务中写盘"
},
"answer": "C",
"explanation": "该时序体现了正确的 WAL:redo log 在提交时持久化(步骤 3),数据页 P 在步骤 5 才延迟刷盘。只要 redo log 先落盘并安全,无论脏页何时刷,崩溃都可通过重放 redo 恢复。A 顺序说反;B、D 与延迟刷盘的 WAL 机制不符。"
},
{
"index": 2,
"type": "short_answer",
"question": "为什么事务提交时只需把 redo log 落盘(而不必把对应数据页落盘),却仍能满足提交后的持久性?",
"answer": "因为提交语义建立在 redo log 的持久化之上:只要 redo 记录已通过 fsync 写在磁盘,即使数据页尚未刷盘,一旦崩溃,InnoDB 重启后也能通过扫描并重放 redo log,把该页的修改恢复到数据文件,从而让已提交(已落 redo)的事务修改永不丢失。数据页落盘可以异步延迟执行,因此提交时只需 redo 落盘,既保证一次成持久性又避免每次 commit 都刷一整页的低效 IO。",
"explanation": "持久性依赖 redo 的持久化而非脏页的当前状态,因此提交时可只 fsync redo,干净保证已提交数据可恢复。",
"keywords": [
"redo 持久化",
"重放恢复",
"脏页延迟刷",
"提交即持久"
],
"scoring_rubric": "总分 4 分:指出 redo 已持久化(1 分);说明崩溃时重放 redo 可恢复该页(2 分);说明避免低效/延迟刷盘(1 分)合计 4 分。"
}
],
"explanation": "本大题考察正确的 WAL 写时序:redo 先行持久化,脏页延迟刷盘,提交即持久依赖 redo。"
},
{
"id": "cr-005",
"type": "code_reading",
"difficulty": 2,
"tags": [
"innodb_flush_log_at_trx_commit",
"持久性",
"性能",
"配置"
],
"question": "请阅读以下配置场景并作答。",
"code": "-- Deep 场景(持续高压写入的 ETL 批量导入)\nSET GLOBAL innodb_flush_log_at_trx_commit = 0;\n-- 期间执行大量 INSERT/UPDATE,然后 MySQL 进程突然崩溃(未正常关机)",
"language": "sql",
"source": null,
"related": [],
"sub_questions": [
{
"index": 1,
"type": "single_choice",
"question": "崩溃重启后,最近约 1 秒内已提交的事务可能出现什么情况?",
"options": {
"A": "所有已提交事务完整保留,一个不丢",
"B": "最近 1 秒内已提交但尚未刷盘的事务可能丢失(被回滚/未恢复),持久性被削弱",
"C": "全部数据丢失,包括很久前已提交的",
"D": "事务数据完全不受影响,只影响查询性能"
},
"answer": "B",
"explanation": "innodb_flush_log_at_trx_commit=0 表示每 1 秒才刷一次 redo log 到磁盘(进程崩溃时未触发的同步点会丢接近 1 秒内的 redo)。因此最近约 1 秒内已“提交”但 redo 尚未落盘的事务,若进程崩溃,将无法通过 redo 恢复,从而缺失(属于主动牺牲持久性换取更高写入吞吐的场景)。A、C、D 与该配置下的行为不符。"
},
{
"index": 2,
"type": "short_answer",
"question": "若将该参数改为 2,在“MySQL 进程崩溃”与“操作系统崩溃”两种情形下,已提交事务丢失情况分别如何?",
"answer": "取值 2 时,redo 每次提交只写入(write,不 fsync)OS 缓存(Page Cache)。因此:MySQL 进程崩溃时,因 OS 仍存活,缓存中的 redo 不丢,已提交事务不丢失;但若是操作系统(或断电等导致缓存丢失)崩溃,则 OS 缓存中的 redo 也可能丢失近 1 秒已提交数据;若要绝对不丢则必须 set 1(每次 fsync)。",
"explanation": "取值 2 写入 OS 缓存的语义:进程崩溃时 OS 缓存完好,数据不丢;OS 崩溃时缓存丢失可能丢接近 1 秒数据,严格持久性需 1。",
"keywords": [
"OS 缓存",
"进程崩溃不丢",
"OS 崩溃可能丢",
"取值 1 严格"
],
"scoring_rubric": "总分 4 分:说明 2 只写 OS 缓存不 fsync(1 分);进程崩溃时不丢(1 分);OS 崩溃可能丢近 1 秒(1 分);严格需要=1(1 分)。"
}
],
"explanation": "本大题考察 innodb_flush_log_at_trx_commit 三取值的持久性与崩溃语义,特别对比 0、2 在不同崩溃类型下的数据差异。"
}
]
}