{ "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": [] } ] }