feat: add database topic group with 105 questions
Deploy Examination / deploy (push) Successful in 4s
Deploy Examination / deploy (push) Successful in 4s
- 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 校验
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"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 在不同崩溃类型下的数据差异。"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
{
|
||||
"topic": "mysql-acid",
|
||||
"type": "fill_blank",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "2026-09-07T00:00:00+08:00",
|
||||
"questions": [
|
||||
{
|
||||
"id": "fb-001",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 1,
|
||||
"tags": [
|
||||
"原子性",
|
||||
"undo log",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "InnoDB 保证事务原子性依赖的日志是 ____,它记录的是修改前的旧值,回滚时据此恢复数据。",
|
||||
"answer": [
|
||||
"undo log"
|
||||
],
|
||||
"answer_rule": "any",
|
||||
"explanation": "undo log 记录的是事务修改前的旧值(逻辑逆操作)。当事务回滚或崩溃恢复时,InnoDB 依据 undo log 将数据恢复到事务开始前的状态,从而保证原子性(要么全部提交要么全部回滚)。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-002",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 1,
|
||||
"tags": [
|
||||
"持久性",
|
||||
"WAL",
|
||||
"redo log"
|
||||
],
|
||||
"question": "InnoDB 保证持久性采用 Write-Ahead Logging 策略,即在数据页落盘前必须先将对应的 ____ 日志写入磁盘。",
|
||||
"answer": [
|
||||
"redo log"
|
||||
],
|
||||
"answer_rule": "any",
|
||||
"explanation": "WAL 的核心是“写日志先行”:任何数据页修改在刷入磁盘之前,必须先把对应的 redo log 记录写入磁盘。这样一旦崩溃,通过重放 redo log 即可恢复未落盘的已提交修改,从而保证持久性。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-003",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"物理日志",
|
||||
"环形缓冲",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "InnoDB 的 redo log 在逻辑上是固定大小的环形缓冲,写入是循环的,当写满时会覆盖最 ____ 的日志。",
|
||||
"answer": [
|
||||
"旧"
|
||||
],
|
||||
"answer_rule": "any",
|
||||
"explanation": "redo log 由若干固定大小的文件组成环形结构,物理日志循环写入,当文件写满会回绕覆盖最旧(最早写入)的日志记录。因此 redo log 并不记录全部历史,而只保留最近一段时间的重做记录。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-004",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"物理日志",
|
||||
"undo log",
|
||||
"逻辑日志"
|
||||
],
|
||||
"question": "redo log 记录的是对数据页字节修改的____日志(物理层),而 undo log 记录的是如何“改回旧值”的____日志(逻辑层)。",
|
||||
"answer": [
|
||||
"物理",
|
||||
"逻辑"
|
||||
],
|
||||
"answer_rule": "ordered",
|
||||
"explanation": "redo log 属于物理日志,描述对哪个页的哪个位置的字节进行了什么修改,用于崩溃后重放;undo log 属于逻辑日志,记录的是把数据改回旧状态所需的逆操作逻辑。两个空位必须按顺序填“物理 / 逻辑”,所以使用 ordered 规则。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-005",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"MVCC",
|
||||
"undo log",
|
||||
"隔离性"
|
||||
],
|
||||
"question": "InnoDB 的 MVCC 通过 undo log 维护行的多____链,配合事务的 ____ 快照决定当前读可见的版本,从而在不加锁的情况下实现一致性的快照读。",
|
||||
"answer": [
|
||||
"版本",
|
||||
"ReadView"
|
||||
],
|
||||
"answer_rule": "ordered",
|
||||
"explanation": "InnoDB 通过 undo log 维护行的多版本链,每次更新原记录时旧版本被放入版本链。一致性读(快照读)通过 ReadView(一个事务范围内的可见性判断结构)沿版本链找到当前事务可见的版本,实现高并发的非锁定读。两空位按顺序填“版本 / ReadView”。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-006",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"崩溃恢复",
|
||||
"redo log",
|
||||
"undo log"
|
||||
],
|
||||
"question": "InnoDB 崩溃恢复的三阶段为:一、扫描 ____ 确定需要恢复的范围;二、对其中的已提交事务进行 ____;三、对未提交事务用 ____ 进行回滚。",
|
||||
"answer": [
|
||||
"redo log",
|
||||
"重做",
|
||||
"undo log"
|
||||
],
|
||||
"answer_rule": "ordered",
|
||||
"explanation": "崩溃恢复流程:先扫描 redo log 确定恢复起点;然后重做(应用)崩溃前已提交但未刷盘的事务,使其修改落到数据页;最后根据两阶段提交状态,对未提交事务用 undo log 回滚,恢复到崩溃前的一致状态。三个空位需按顺序作答。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-007",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"binlog",
|
||||
"redo log",
|
||||
"两阶段提交"
|
||||
],
|
||||
"question": "为协调 InnoDB 与 binlog 的一致性,MySQL 对两者采用____阶段提交:先将 redo log 写入____(prepare)阶段,再写 binlog,最后把 redo 标记为 commit。",
|
||||
"answer": [
|
||||
"两",
|
||||
"prepare"
|
||||
],
|
||||
"answer_rule": "ordered",
|
||||
"explanation": "MySQL 对 redo log 与 binlog 用两阶段提交(2PC / InnoDB&binlog 内部协调):第一步把 redo log 写入 prepare 阶段(此刻事务处于预提交),第二步写 binlog,第三步将 redo 标记为 commit。崩溃重启时依据 binlog 是否完整来决定事务是提交还是回滚,从而保证两者一致。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-008",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"innodb_flush_log_at_trx_commit",
|
||||
"持久性"
|
||||
],
|
||||
"question": "innodb_flush_log_at_trx_commit=1 时,每次事务提交都会调用 ____ 强制把 redo log 刷到磁盘,保证最强的持久性(也最浪费 IO)。",
|
||||
"answer": [
|
||||
"fsync"
|
||||
],
|
||||
"answer_rule": "any",
|
||||
"explanation": "取值 1 意味着每次提交时 InnoDB 都调用 fsync 把 redo log 从 OS 缓存强制刷入磁盘,确保提交的事务真正持久化,因此符合严格 ACID 的持久性要求,但会带来较多磁盘同步 IO。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-009",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"binlog",
|
||||
"主从复制",
|
||||
"审计"
|
||||
],
|
||||
"question": "binlog 是 MySQL ____ 层的逻辑日志,主要用于主从复制、基于时间点恢复(PITR)和 ____;而 redo log 是 InnoDB 引擎层用于崩溃恢复的日志。",
|
||||
"answer": [
|
||||
"Server",
|
||||
"审计"
|
||||
],
|
||||
"answer_rule": "any",
|
||||
"explanation": "binlog 由 MySQL 的 Server 层记录逻辑变更,其主要用途包括主从复制(从库重放 binlog)、基于时间点恢复(PITR)以及审计。而 redo log 属于 InnoDB 存储引擎层,只用于崩溃恢复时重做已提交事务。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "fb-010",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"Buffer Pool",
|
||||
"脏页",
|
||||
"刷盘",
|
||||
"WAL"
|
||||
],
|
||||
"question": "Buffer Pool 中被修改而未刷盘的数据页称为 ____ 页,InnoDB 会延迟到合适时机(如 CHECKPOINT)才批量 ____ 并将其落盘;由于 redo log 已先行持久化,即使这期间崩溃也能通过重放恢复。",
|
||||
"answer": [
|
||||
"脏",
|
||||
"刷盘"
|
||||
],
|
||||
"answer_rule": "ordered",
|
||||
"explanation": "Buffer Pool 中已修改但尚未写回磁盘的页称为脏页。InnoDB 通常在检查点(checkpoint)或后台刷脏线程等时机批量刷盘,而不是每次修改都立即落盘。因为 redo log 已先持久化,脏页即便在刷盘前崩溃,也能通过重放 redo 恢复,这正是 WAL + 延迟刷盘配合的高效设计。",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"slug": "mysql-acid",
|
||||
"name": "MySQL ACID 实现机制",
|
||||
"description": "InnoDB 如何用日志与锁实现事务的原子性/一致性/隔离性/持久性:undo log、redo log、WAL、MVCC、锁",
|
||||
"tags": [
|
||||
"ACID",
|
||||
"InnoDB",
|
||||
"MVCC",
|
||||
"WAL",
|
||||
"binlog",
|
||||
"innodb_flush_log_at_trx_commit",
|
||||
"next-key lock",
|
||||
"redo log",
|
||||
"undo log",
|
||||
"一致性",
|
||||
"两阶段提交",
|
||||
"原子性",
|
||||
"崩溃恢复",
|
||||
"快照读",
|
||||
"性能",
|
||||
"持久性",
|
||||
"物理日志",
|
||||
"约束",
|
||||
"行锁",
|
||||
"逻辑日志",
|
||||
"间隙锁",
|
||||
"隔离性"
|
||||
],
|
||||
"difficulty_range": [
|
||||
2,
|
||||
4
|
||||
],
|
||||
"schema_version": "1.0.0",
|
||||
"updated": "2026-09-07",
|
||||
"question_files": [
|
||||
"single_choice",
|
||||
"fill_blank",
|
||||
"code_reading",
|
||||
"short_answer"
|
||||
],
|
||||
"stats": {
|
||||
"total": 35,
|
||||
"by_type": {
|
||||
"single_choice": 10,
|
||||
"fill_blank": 10,
|
||||
"code_reading": 5,
|
||||
"short_answer": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"topic": "mysql-acid",
|
||||
"type": "short_answer",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "2026-09-07T00:00:00+08:00",
|
||||
"questions": [
|
||||
{
|
||||
"id": "sa-001",
|
||||
"type": "short_answer",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"ACID",
|
||||
"原子性",
|
||||
"undo log",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "解释 InnoDB 如何用 undo log 实现事务的原子性(回滚)。",
|
||||
"answer": "InnoDB 在每个事务执行写操作时,先把旧值(修改前的数据)写入 undo log。若事务需要回滚或崩溃恢复时确定未提交,InnoDB 依据 undo log 中记录的旧值做逆操作:INSERT 对应 DELETE 反操作、DELETE 对应重新插入、UPDATE 则把新旧值互换改回,从而把数据恢复到事务开始前的状态。整个事务要么全部提交要么全部回滚,即为原子性。",
|
||||
"keywords": [
|
||||
"undo log",
|
||||
"旧值",
|
||||
"逆序回滚",
|
||||
"原子性"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:说明修改前写 undo log(2 分);说明用旧值逆序回滚(2 分)。",
|
||||
"explanation": "核心是“记录旧值 + 逆操作回滚”。答出 undo log 记录旧值、支持把数据恢复到事务前状态即可得分。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-002",
|
||||
"type": "short_answer",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"持久性",
|
||||
"redo log",
|
||||
"WAL",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "什么是 Write-Ahead Logging(WAL)?它在 MySQL InnoDB 中如何保证持久性?",
|
||||
"answer": "WAL 指“先写日志再写数据”:任何数据页的修改在落盘之前,必须先把对应的 redo log 记录写入磁盘并持久化。InnoDB 修改 Buffer Pool 中的页时会先向 redo log 追加物理变更;事务提交时确保该 redo 已写入磁盘(fsync,或按 innodb_flush_log_at_trx_commit 决定)。若在数据页落盘前崩溃,重启时通过扫描并重放 redo log,把已提交事务的修改恢复到数据文件,因此已提交数据不会丢失,从而保证持久性。",
|
||||
"keywords": [
|
||||
"WAL",
|
||||
"先写日志再写数据",
|
||||
"redo log",
|
||||
"重放恢复",
|
||||
"持久性"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:给出 WAL 定义“先写日志后写数据”(2 分);说明崩溃重放 redo 恢复(1 分);结合 InnoDB 描述(1 分)。",
|
||||
"explanation": "先陈述 WAL 的顺序要点,再联系 redo log 在这次重放(redo)与 fsync 的细节,符合本主题核心理解。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-003",
|
||||
"type": "short_answer",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"物理日志",
|
||||
"undo log",
|
||||
"逻辑日志"
|
||||
],
|
||||
"question": "对比 redo log 与 undo log 在记录内容(物理/逻辑)和作用上的区别。",
|
||||
"answer": "两者差异如下:1)记录内容:redo log 是物理日志,记录了对哪个数据页哪一处的字节所做的实际修改;undo log 是逻辑日志,记录如何把一行数据“改回旧值”的逆操作。2)作用方向:redo 用于崩溃后重做(REDO)已提交事务,保证持久性;undo 用于回滚未提交事务(或 MVCC 提供历史版本)保证原子性与隔离读。redo 是能重做的物理重放,undo 是可逆回滚的逻辑逆操作。",
|
||||
"keywords": [
|
||||
"redo 物理",
|
||||
"undo 逻辑",
|
||||
"重做已提交",
|
||||
"回滚未提交",
|
||||
"崩溃恢复"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:redo 物理日志(1 分);undo 逻辑日志(1 分);redo 重做已提交(1 分);undo 回滚未提交(1 分)。",
|
||||
"explanation": "分别从“物理 vs 逻辑”“重做 vs 回滚”两个维度对比,即可覆盖核心差异。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-004",
|
||||
"type": "short_answer",
|
||||
"difficulty": 4,
|
||||
"tags": [
|
||||
"崩溃恢复",
|
||||
"redo log",
|
||||
"undo log",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "详细描述 InnoDB 崩溃恢复的三阶段流程。",
|
||||
"answer": "第一阶段(扫描 redo log):从 redo 中确定本次恢复需要处理的 lSN 范围与 checkpoint 位置,找出需要重做的日志段。第二阶段(重做已提交事务):按 redo log 将崩溃前已提交但尚未刷盘到数据文件的数据页修改重新应用(redo ),使这些已提交的修改得到恢复。第三阶段(回滚未提交事务):通过两阶段提交记录的 xid 识别哪些事务未在 binlog 中完整提交,利用 undo log 把这些未提交事务的执行进行回滚,最终使数据库恢复到崩溃前一致、没有丢失已提交数据且无未提交改动残留的状态。",
|
||||
"keywords": [
|
||||
"扫描 redo",
|
||||
"重做已提交",
|
||||
"回滚未提交",
|
||||
"两阶段提交",
|
||||
"checkpoint"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:扫描 redo 确定范围(1 分);重做已提交事务(2 分);用 undo 回滚未提交(1 分)。",
|
||||
"explanation": "按顺序答出重做/回滚的职责与对象判断(以两阶段提交/是否已提交为依据)即可。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-005",
|
||||
"type": "short_answer",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"隔离性",
|
||||
"MVCC",
|
||||
"undo log",
|
||||
"快照读"
|
||||
],
|
||||
"question": "MVCC 是什么?InnoDB 使用它如何提升并发隔离性?",
|
||||
"answer": "MVCC(多版本并发控制)是 InnoDB 提供一致性非锁定读的机制:通过 undo log 维护每一行的多版本链。当普通 SELECT(快照读)执行时,InnoDB 依据事务的 ReadView(可见性判断)沿版本链找到当前事务可见版本,不需要加共享锁。这样多个读事务与写事务可并发执行,读不被写阻塞,从而在维持隔离性的前提下显著提升并发吞吐。",
|
||||
"keywords": [
|
||||
"MVCC",
|
||||
"undo log 版本链",
|
||||
"ReadView",
|
||||
"一致性读",
|
||||
"非锁读"
|
||||
],
|
||||
"scoring_rubric": "总分 3 分:给出 MVCC 定义(1 分);说明 undo 多版本 + ReadView(1 分);读不加锁、提升并发(1 分)。",
|
||||
"explanation": "抓住“多版本链 + ReadView + 读不加锁”即可说明 MVCC 是如何提升并发也保隔离的。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-006",
|
||||
"type": "short_answer",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"隔离性",
|
||||
"行锁",
|
||||
"间隙锁",
|
||||
"next-key lock"
|
||||
],
|
||||
"question": "在可重复读(RR)隔离级别下,InnoDB 的 next-key lock(临键锁)如何防止幻读?",
|
||||
"answer": "RR 下 InnoDB 对索引范围/查找会加 next-key lock(由行锁 + 间隙锁组成的左开右闭区间锁)。它既锁住命中的记录(行锁),又锁住记录之间的间隙(gap),阻止其他事务在该扫描区间内 INSERT 新行。因为在该被锁定的范围里不可能再插入新记录,当前事务多次查询同一范围结果一致,从而防止了幻读。",
|
||||
"keywords": [
|
||||
"next-key lock",
|
||||
"gap lock",
|
||||
"锁定区间",
|
||||
"防幻读",
|
||||
"RR"
|
||||
],
|
||||
"scoring_rubric": "总分 3 分:指出 next-key = 行锁 + 间隙锁(1 分);锁区间阻止插入(1 分);讲到幻读消除(1 分)。",
|
||||
"explanation": "从一个锁出发:锁间区使别人无法插入,故范围查询结果稳定,防幻读。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-007",
|
||||
"type": "short_answer",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"两阶段提交",
|
||||
"redo log",
|
||||
"binlog",
|
||||
"一致性"
|
||||
],
|
||||
"question": "为什么 MySQL 需要用两阶段提交来协调 redo log 与 binlog?",
|
||||
"answer": "因为同一条事务的修改既要写 redo log(用于崩溃恢复)又要写 binlog(用于复制与恢复),若没协调,崩溃可能发生在一个日志已写另一个未写的时刻,导致主库重放后与从库/恢复点不一致。两阶段提交通过 prepare(redo 先 prepare)+写 binlog+ commit(redo 标记)的划分,并在恢复时以“binlog 是否完整作为事务是否提交”的裁决,保证 redo 与 binlog 的已提交事务集合完全一致,杜绝主从数据不一致与 PITR 偏差。",
|
||||
"keywords": [
|
||||
"两阶段提交",
|
||||
"prepare",
|
||||
"binlog 为准",
|
||||
"主从一致",
|
||||
"xa"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:指出两份日志都需要(1 分);说明子过程 prepare/binlog/commit(1 分);说明以 binlog 为准裁决提交(2 分)。",
|
||||
"explanation": "核心是“防止在日志中间崩溃造成两份日志事务集不一致”,并以 binlog 为裁决者。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-008",
|
||||
"type": "short_answer",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"binlog"
|
||||
],
|
||||
"question": "redo log 与 binlog 各自的应用场景和作用是什么?",
|
||||
"answer": "redo log 是 InnoDB 引擎层专用于崩溃恢复的重做日志(物理记录页修改);binlog 是 MySQL Server 层的逻辑日志,记录事务的逻辑变更,主要场景为主从复制(从库重放 binlog)、基于时间点/位置的恢复 PITR 及审计。redo 在崩溃恢复时让 InnoDB 持久化已提交数据,binlog 面向整个实例的外部同步与运维。",
|
||||
"keywords": [
|
||||
"redo 崩溃恢复",
|
||||
"binlog 复制",
|
||||
"PITR",
|
||||
"审计"
|
||||
],
|
||||
"scoring_rubric": "总分 3 分:redo 崩溃重做(1 分);binlog 复制 PITR(1 分);指出层与外部区别(1 分)。",
|
||||
"explanation": "从时间/层和用途三方面对比即可。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-009",
|
||||
"type": "short_answer",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"一致性",
|
||||
"约束",
|
||||
"ACID"
|
||||
],
|
||||
"question": "解释在 InnoDB 中一致性(Consistency)是如何实现的,并说明它与 A、I、D 的关系。",
|
||||
"answer": "一致性指事务保证数据从一致状态到一致的状态。InnoDB 通过约束(主键、唯一、非空、外键、CHECK 等)保证提交的数据满足完整性,同时一致性依赖于另三者:若原子性被破坏会造成部分写入,隔离性破坏可能读到中间态,持久性破坏会导致状态回退。只有当原子性(全提交或全回滚)、隔离性(并发不受互扰)、持久性(提交可恢复)三者共同成立,配合约束与业务逻辑,才能把已提交事务改变成一个整体一致的状态。",
|
||||
"keywords": [
|
||||
"约束",
|
||||
"原子性",
|
||||
"隔离性",
|
||||
"持久性",
|
||||
"一致状态"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:提到约束(1 分);A/I/D 各自如何维护一致(2 分);说明三者共同保证(1 分)。",
|
||||
"explanation": "一致性不是一个独立机制,而是约束 + A/I/D 的协作者;答出三者影响一致性的链路即好。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sa-010",
|
||||
"type": "short_answer",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"innodb_flush_log_at_trx_commit",
|
||||
"持久性",
|
||||
"性能"
|
||||
],
|
||||
"question": "对比 innodb_flush_log_at_trx_commit 取 (0/1/2) 三种值在持久性与性能上的取舍。",
|
||||
"answer": "0:每秒刷一次 redo log 盘,性能直高但进程崩溃会丢近 1 秒已提交事务;1:每次提交都 fsync 刷盘,持久性最强、最安全,但磁盘同步 IO 开销大、提交延迟高;2:每次提交把 redo 写至 OS 缓存(write,不 fsync),MySQL 进程崩溃时数据不丢,但 OS 崩溃/断电可能丢近 1 秒数据,性能比起 1 明显更高、比 0 稍低。生产 Server 建议设置为 1(严格 ACID),对可容忍少量丢亦可 2/0 提升吞吐。",
|
||||
"keywords": [
|
||||
"0 每秒刷盘",
|
||||
"1 每次 fsync",
|
||||
"2 OS 缓存",
|
||||
"持久性 vs 性能"
|
||||
],
|
||||
"scoring_rubric": "总分 4 分:0 语义与丢数据(1 分);1 语义持久性最强(1 分);2 写 OS 缓存语义(1 分);指出取舍与推荐(1 分)。",
|
||||
"explanation": "三个取值逐一说明“何时同步 fsync”即可,再补充其与几种崩溃类型的对应关系。",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"topic": "mysql-acid",
|
||||
"type": "single_choice",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "2026-09-07T00:00:00+08:00",
|
||||
"questions": [
|
||||
{
|
||||
"id": "sc-001",
|
||||
"type": "single_choice",
|
||||
"difficulty": 1,
|
||||
"tags": [
|
||||
"ACID",
|
||||
"原子性",
|
||||
"undo log",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "InnoDB 中,事务回滚时依赖哪种日志来恢复旧值,从而保证 ACID 中的原子性?",
|
||||
"options": {
|
||||
"A": "redo log",
|
||||
"B": "binlog",
|
||||
"C": "undo log",
|
||||
"D": "slow query log"
|
||||
},
|
||||
"answer": "C",
|
||||
"explanation": "undo log 记录的是事务修改前的旧值(逻辑逆操作),当事务回滚或崩溃恢复时用它把数据恢复原状,保证原子性(要么全部执行要么全部回滚)。redo log 用于崩溃恢复时重做已提交事务,保证持久性;binlog 是 Server 层的逻辑日志,用于复制和 PITR;slow query log 只是性能诊断日志,与原子性无关。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-002",
|
||||
"type": "single_choice",
|
||||
"difficulty": 1,
|
||||
"tags": [
|
||||
"持久性",
|
||||
"redo log",
|
||||
"WAL",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "MySQL InnoDB 的 WAL(Write-Ahead Logging)核心思想是:",
|
||||
"options": {
|
||||
"A": "先写数据文件再写日志文件",
|
||||
"B": "先写 redo log,再刷数据页,崩溃后通过 redo 重放保证持久性",
|
||||
"C": "只在内存中修改数据,从不落盘",
|
||||
"D": "每次修改直接覆盖数据文件"
|
||||
},
|
||||
"answer": "B",
|
||||
"explanation": "WAL 要求任何数据页修改落盘之前,先把对应的 redo log 记录写入磁盘并保证其持久化(fsync 或满足配置条件)。这样即便数据页还没刷盘就发生崩溃,重启后也能通过重放 redo log 恢复已提交的数据,从而保证持久性。A、D 违背 WAL 顺序;C 显然错误,数据最终要落盘。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-003",
|
||||
"type": "single_choice",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"物理日志",
|
||||
"环形缓冲",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "关于 InnoDB redo log 的说法,正确的是:",
|
||||
"options": {
|
||||
"A": "redo log 是固定大小的循环写入的物理日志,写满后会覆盖最旧的日志",
|
||||
"B": "redo log 可以无限增长,永远不会被覆盖",
|
||||
"C": "redo log 记录的是 SQL 语句本身(逻辑日志)",
|
||||
"D": "redo log 存储在系统变量中,不落盘"
|
||||
},
|
||||
"answer": "A",
|
||||
"explanation": "InnoDB 的 redo log 在逻辑上是一个环形缓冲(固定大小,由 innodb_log_file_size 决定),写入是循环的,写满时会覆盖最旧的日志。它是物理日志(记录对哪个页哪一处的实际字节修改),而非逻辑日志。日志需要持久化到磁盘才能发挥作用,所以 C、D 错误。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-004",
|
||||
"type": "single_choice",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"redo log",
|
||||
"binlog",
|
||||
"两阶段提交",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "Redo log 与 binlog 的主要区别在于:",
|
||||
"options": {
|
||||
"A": "两者完全相同,只是名字不同",
|
||||
"B": "redo log 是 InnoDB 引擎层用于崩溃恢复,binlog 是 Server 层用于主从复制、PITR 和审计",
|
||||
"C": "binlog 用于崩溃恢复,redo log 用于主从复制",
|
||||
"D": "redo log 记录逻辑 SQL,binlog 记录物理页修改"
|
||||
},
|
||||
"answer": "B",
|
||||
"explanation": "redo log 是 InnoDB 特有的物理重做日志,只在崩溃恢复时重放已提交事务;binlog 是 MySQL Server 层的逻辑日志(记录对数据库的逻辑改动),服务于主从复制、基于时间点的恢复(PITR)和审计。C 把职责颠倒了。redo log 是物理的、binlog 是逻辑的,所以 D 也错。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-005",
|
||||
"type": "single_choice",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"两阶段提交",
|
||||
"redo log",
|
||||
"binlog",
|
||||
"一致性"
|
||||
],
|
||||
"question": "在 InnoDB 与 binlog 之间使用两阶段提交(2PC)的主要目的是:",
|
||||
"options": {
|
||||
"A": "提升写入性能,减少磁盘 IO",
|
||||
"B": "保证 redo log 与 binlog 的一致性,避免崩溃后主从数据不一致",
|
||||
"C": "加快崩溃恢复速度",
|
||||
"D": "减少内存占用"
|
||||
},
|
||||
"answer": "B",
|
||||
"explanation": "同一事务会同时写 redo log 和 binlog。两阶段提交把写入过程分成 prepare(redo 写 prepare 阶段)和 commit,崩溃恢复时以 binlog 是否完整生成(xa 事务状态)为准来决定事务该提交还是回滚,从而保证两份日志的一致,避免主库已提交但从库/恢复时出现不一致。它不提升写入性能,也不直接加速恢复或省内存,所以 A、C、D 不对。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-006",
|
||||
"type": "single_choice",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"MVCC",
|
||||
"隔离性",
|
||||
"undo log",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "InnoDB 的 MVCC(多版本并发控制)实现中,普通 SELECT(非 locking read)读取的历史版本数据存储在哪个结构中?",
|
||||
"options": {
|
||||
"A": "redo log",
|
||||
"B": "Buffer Pool 的一处独立缓冲",
|
||||
"C": "undo log(历史版本链)",
|
||||
"D": "binlog 的历史记录"
|
||||
},
|
||||
"answer": "C",
|
||||
"explanation": "InnoDB 通过 undo log 维护行的多版本链:每次更新会保留旧版本,当前读(快照读)根据事务的 ReadView 沿着 undo 版本链找到事务可见的历史版本,从而实现非锁定的一致性读,提升并发。redo log 是物理重做,binlog 是逻辑日志,Buffer Pool 存的是页而非版本链,均不负责多版本数据。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-007",
|
||||
"type": "single_choice",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"崩溃恢复",
|
||||
"redo log",
|
||||
"undo log",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "InnoDB 崩溃恢复的三阶段顺序正确的是:",
|
||||
"options": {
|
||||
"A": "扫描 redo log → 用 undo 回滚未提交事务 → 重做已提交事务",
|
||||
"B": "扫描 redo log → 重做(应用)已提交事务 → 用 undo 回滚未提交事务",
|
||||
"C": "先备份数据 → 再扫描 redo → 最后刷脏页",
|
||||
"D": "先用 undo 重做 → 再用 redo 回滚"
|
||||
},
|
||||
"answer": "B",
|
||||
"explanation": "正确的三阶段是:首先扫描 redo log 确定需要恢复的 LSN 范围;然后重放(重做)已提交事务的 redo,使崩溃前已提交但未刷盘的修改恢复到数据页;最后通过两阶段提交的 xid 检查,用 undo log 回滚那些未提交(未在 binlog 中完整记录)的事务。A 和 D 顺序反了。C 是无关操作。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-008",
|
||||
"type": "single_choice",
|
||||
"difficulty": 2,
|
||||
"tags": [
|
||||
"innodb_flush_log_at_trx_commit",
|
||||
"持久性",
|
||||
"性能",
|
||||
"配置"
|
||||
],
|
||||
"question": "参数 innodb_flush_log_at_trx_commit=2 时,日志刷盘行为是:",
|
||||
"options": {
|
||||
"A": "每次提交都调用 fsync 强制刷到磁盘",
|
||||
"B": "每秒刷一次磁盘,崩溃可能丢失近 1 秒已提交事务",
|
||||
"C": "每次提交时只把日志写入操作系统缓存(Page Cache),由 OS 决定何时落盘,崩溃可能丢失近 1 秒已提交事务",
|
||||
"D": "不写日志,完全不做持久化"
|
||||
},
|
||||
"answer": "C",
|
||||
"explanation": "innodb_flush_log_at_trx_commit 有三个取值:0=每秒刷盘一次(可能丢接近 1 秒数据);1=每次事务提交都 fsync 刷盘(最强持久性,也是 ACID 默认保证,推荐);2=每次提交只写入 OS 缓存(write,不 fsync),操作系统崩溃时可能丢接近 1 秒已提交事务,但 MySQL 进程崩溃时不会丢。A 是取值 1,B 是取值 0。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-009",
|
||||
"type": "single_choice",
|
||||
"difficulty": 3,
|
||||
"tags": [
|
||||
"隔离性",
|
||||
"临键锁",
|
||||
"间隙锁",
|
||||
"next-key lock",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "在 RR(可重复读)隔离级别下,InnoDB 对普通索引范围查询加的是哪种锁,用来防止幻读?",
|
||||
"options": {
|
||||
"A": "只加行锁(record lock)",
|
||||
"B": "临键锁(Next-Key Lock,行锁 + 间隙锁),锁定记录及其前面的间隙",
|
||||
"C": "表级意向锁",
|
||||
"D": "不加任何锁(快照读自动无锁)"
|
||||
},
|
||||
"answer": "B",
|
||||
"explanation": "在可重复读(RR)隔离级别下,InnoDB 对索引范围/普通查找使用临键锁(next-key lock = 行锁 + 间隙锁),相当于锁定左开右闭的区间,阻止其他事务在扫描区间插入新行,从而防止幻读。纯行锁(A)无法防幻读;意向锁(C)是表级锁标记,不直接防幻读;快照读虽然一般不加锁,但此题针对范围查询加锁场景,答案为 B。",
|
||||
"source": null,
|
||||
"related": []
|
||||
},
|
||||
{
|
||||
"id": "sc-010",
|
||||
"type": "single_choice",
|
||||
"difficulty": 4,
|
||||
"tags": [
|
||||
"一致性",
|
||||
"约束",
|
||||
"ACID",
|
||||
"InnoDB"
|
||||
],
|
||||
"question": "从 ACID 的角度看,InnoDB 的“一致性”主要是由哪部分保证的?",
|
||||
"options": {
|
||||
"A": "完全由数据库自动保证,与应用无关",
|
||||
"B": "由约束(主键、外键、唯一、非空、CHECK 等)保证,并且依赖原子性、隔离性、持久性共同维持事务前后一致的完整状态",
|
||||
"C": "只靠锁机制保证,与约束无关",
|
||||
"D": "只靠 redo log 保证"
|
||||
},
|
||||
"answer": "B",
|
||||
"explanation": "一致性(C)需要一个事务从一致的状态出发最终到另一个一致状态,它由数据库的约束(主外键、唯一、非空、CHECK)以及底层对每个原子、隔离、持久性共同实现:原子性或隔离的破坏都会破坏一致性。所以一致性不是某个单一机制,而是约束 + A、I、D 三者的合力。C、D 只提单一机制不完整,A 过于绝对(业务也要保证业务规则一致)。",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user