Files
examination/topics/database/mysql-acid/single_choice.json
T

229 lines
10 KiB
JSON
Raw Normal View History

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