11 KiB
tags, create time
| tags | create time | ||||||
|---|---|---|---|---|---|---|---|
|
2026-08-09 12:00 |
事务隔离级别与锁机制_测试题
概述
本测试覆盖 InnoDB 的四种事务隔离级别、行锁/间隙锁/临键锁的关系、锁兼容矩阵、死锁检测机制和 Insert Intention Gap Lock。共 10 道题:6 道选择题、3 道填空题、1 道简答题。
一、选择题(6道,由浅入深)
难度阶梯: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
Q1(基础)— 考察定义层面
InnoDB 默认使用的隔离级别是什么?
A. READ UNCOMMITTED B. READ COMMITTED C. REPEATABLE READ D. SERIALIZABLE
Q2(基础)→ 考察行为判断
关于 Next-Key Lock 的描述,以下哪项是正确的?
A. Next-Key Lock = Record Lock + Record Lock(同一行的两个锁) B. Next-Key Lock = Record Lock + Gap Lock,锁定前开后闭区间 C. Next-Key Lock 只锁定索引记录本身,不包含间隙 D. Next-Key Lock 只在 RC 隔离级别下生效
Q3(进阶)→ 考察原理理解
使用**唯一索引(如主键)**进行等值查询时,InnoDB 加的锁类型是:
A. Next-Key Lock(Record + Gap) B. Record Lock(仅锁定该行) C. Gap Lock(锁定前后间隙) D. Table Lock(整张表锁住)
Q4(进阶)→ 考察比较辨析
RC 和 RR 在锁机制上的本质区别之一是什么?
A. RC 加排他锁,RR 加共享锁 B. RC 关闭了 Gap Lock,只加 Record Lock;RR 默认使用 Next-Key Lock C. RC 没有 undo log,RR 有 undo log D. RC 不支持死锁检测,RR 支持
Q5(深入)→ 考察场景推理
索引中有记录 id IN (5, 10, 15),事务 A 执行 UPDATE orders SET status = 1 WHERE id > 5 AND id < 15;。此时另一个事务 B 尝试 INSERT INTO orders (id) VALUES (12);,结果如何?
A. 事务 B 立即成功,因为 id=12 不是现有记录 B. 事务 B 被阻塞,因为 (10, 15) 间隙已被事务 A 用 Next-Key Lock 锁定 C. 事务 B 立即成功,因为 UPDATE 只对已有记录加锁 D. 事务 B 被阻塞,但原因是 INSERT 本身需要表级排他锁
Q6(深入)→ 考察源码级细节
关于 InnoDB 的死锁处理,下列说法错误的是:
A. InnoDB 使用等待图(Wait-For Graph)进行死锁检测
B. 检测到环路时,选择回滚代价最大的事务作为牺牲者
C. 回滚的是"当前语句"而非"整个事务"
D. innodb_lock_wait_timeout 默认值为 50 秒,控制非死锁场景下的锁等待超时
二、填空题(3道)
F1 — 锁类型填空
| 锁类型 | 锁定范围 | 描述 |
|---|---|---|
| Record Lock | ______ | 单行精确锁定 |
| Gap Lock | 索引记录之间的间隙,______记录本身 | 防止其他事务在该间隙插入新记录 |
| Next-Key Lock | Record Lock + Gap Lock 的组合 | 锁定______区间(前开后闭),这是 InnoDB 的默认锁粒度 |
提示: 回忆三种锁类型的定义表格——Gap Lock 不含记录本身,Next-Key Lock 是前开后闭的半开区间。
F2 — 锁兼容矩阵
已知排他锁(X Lock)与任何锁都不兼容,那么:
| 已有锁 ↓ \ 请求锁 → | S 锁(共享锁) | X 锁(排他锁) |
|---|---|---|
| S 锁(已有) | ______(兼容/冲突) | ______(兼容/冲突) |
| X 锁(已有) | ______(兼容/冲突) | ______(兼容/冲突) |
INSERT / UPDATE / DELETE 隐式加______锁;SELECT ... FOR UPDATE 显式加______锁;SELECT ... LOCK IN SHARE MODE 加______锁。
提示: S 锁与其他 S 锁兼容但与 X 锁冲突;X 锁独占,不与任何锁兼容。
F3 — 参数配置
| 参数名 | 默认值 | 含义 |
|---|---|---|
| innodb_lock_wait_timeout | ______秒 | 等待锁的超时时间(不涉及死锁检测) |
| innodb_deadlock_detect | ______ | 是否开启死锁检测 |
| innodb_max_dirty_pages_pct | ______% | 脏页比例阈值,超过时强制刷新 |
提示: 这些是 InnoDB 的关键运行时参数,默认值需要在生产环境中根据实际情况调优。
三、简答题(1道)
S1
某电商系统在秒杀活动中出现死锁问题。分析以下两个并发会话的操作序列:
-- 会话 A(库存扣减)
START TRANSACTION;
UPDATE inventory SET stock = stock - 1
WHERE product_id = 1001 AND warehouse_id = 1;
-- 同时插入订单记录
INSERT INTO orders (product_id, user_id, warehouse_id, total_price)
VALUES (1001, U100, 1, 299.00);
COMMIT;
-- 会话 B(相同操作,不同用户)
START TRANSACTION;
UPDATE inventory SET stock = stock - 1
WHERE product_id = 1001 AND warehouse_id = 2;
INSERT INTO orders (product_id, user_id, warehouse_id, total_price)
VALUES (1001, U200, 2, 299.00);
COMMIT;
请回答:
- 如果两条 SQL 的执行顺序不一致(例如先 INSERT 再 UPDATE),什么情况下会产生死锁?画出等待图说明
- Insert Intention Gap Lock 在 INSERT 操作中起什么作用?它为什么不会与其他 INSERT 产生冲突?
- 给出减少此类死锁的方案设计建议
答题框架提示: 分析两会话的锁依赖关系 → 识别循环等待链 → 理解 Insert Intention 锁的特殊性 → 从执行顺序、隔离级别、批量策略角度提方案。
参考答案与解析
选择题答案
| 题号 | 正确答案 | 解析 |
|---|---|---|
| Q1 | C | InnoDB 默认隔离级别是 REPEATABLE READ。这是 MySQL 为了增强数据一致性而采用的默认值,比标准的 SQL 隔离级别更严格(通过 MVCC + Next-Key Lock 解决了大部分幻读问题)。READ UNCOMMITTED 几乎没人用;READ COMMITTED 常用于 PostgreSQL 默认;SERIALIZABLE 性能太差。 |
| Q2 | B | Next-Key Lock = Record Lock(锁定索引记录本身)+ Gap Lock(锁定记录之间的间隙),形成前开后闭区间,如 (5, 10]。A 错在重复自身;C 描述的是纯 Record Lock(唯一索引等值查询时的行为);D 错在 Next-Key Lock 是 RR 的特性,RC 关闭了 Gap Lock。 |
| Q3 | B | 当使用唯一索引(如主键)进行等值查询时,InnoDB 知道只会锁定一行,因此会去掉 Gap Lock,只剩 Record Lock。这样可以减少不必要的间隙锁定,提高并发度。A 是非唯一索引或范围查询的情况;C 不完整;D 错误。 |
| Q4 | B | RC 和 RR 在锁层面的本质区别是:RC 关闭了 Gap Lock,只加 Record Lock;RR 默认使用 Next-Key Lock(Record + Gap 组合)。这意味着在 RC 下 INSERT 操作不会被阻塞(因为没有间隙锁),但 RC 不能解决幻读问题。这是为什么高并发插入场景中考虑降低到 RC 的原因。 |
| Q5 | B | UPDATE ... WHERE id > 5 AND id < 15 会对满足条件的扫描路径加 Next-Key Lock。具体为 (5, 10] 和 (10, 15],其中 (10, 15) 这个间隙被 Gap Lock 覆盖。事务 B 的 INSERT id=12 目标落在 (10, 15) 间隙中,因此会被阻塞。这是 Next-Key Lock 防止幻读的核心体现。 |
| Q6 | B | 死锁检测中,InnoDB 选择的是回滚代价最小的事务作为牺牲者,而不是代价最大的。代价计算通常基于已回滚的数据量大小。A 正确,使用 Wait-For Graph 检测环路;C 正确,只回滚当前语句而非整个事务;D 正确,默认 50 秒。B 说反了是最关键的错误。 |
填空题答案
| 题号 | 答案 | 解析 |
|---|---|---|
| F1 | 索引记录本身;不含;半开 | Record Lock 精确锁定单条索引记录;Gap Lock 锁定间隙但不含记录本身;Next-Key Lock 形成半开区间 (a, b](前开后闭),即包含右端点 b 的记录但不包含左端点 a。 |
| F2 | 兼容;冲突;冲突;冲突;X(排他);X(排他);S(共享) | S 锁允许并发读取所以兼容;S 与 X 冲突因为写入独占资源;X 与所有锁都冲突因为是独占的;INSERT/UPDATE/DELETE 隐式加 X 锁;FOR UPDATE 显式 X 锁;LOCK IN SHARE MODE 加 S 锁。 |
| F3 | 50;ON;75 | innodb_lock_wait_timeout 默认 50 秒(非死锁等待超时);deadlock_detect 默认开启以自动检测并解决死锁;dirty pages 阈值 75% 控制刷脏频率。这三个参数在高并发写入场景下都需要重点关注和调优。 |
简答题参考答案
S1 参考答案要点:
-
死锁条件与等待图:
- 如果会话 A 先执行 UPDATE 加锁 inventory(1001,1),再执行 INSERT 需要加 orders 间隙锁
- 如果会话 B 先执行 INSERT 需要加 inventory(1001,2) 的 Insert Intention 锁
- 关键死锁场景:当两个会话对同一张表的同一索引范围加锁且顺序相反时
具体死锁路径示例:
假设两会话都操作 product_id=1001, warehouse_id=1 的记录: 会话 A: UPDATE → 持有 inventory record(1001,1) 的 X 锁 会话 B: UPDATE → 持有 inventory record(1001,1) 的 X 锁 → 等待 A 实际上同一记录的 UPDATE 会导致阻塞而非死锁。真正产生死锁的典型场景: 会话 A: UPDATE inventory(...) WHERE pk=1 → 加 Record Lock on row 1 INSERT INTO orders(pk=1) → 申请 Index Gap Lock → 等会话 B 会话 B: UPDATE inventory(...) WHERE pk=2 → 加 Record Lock on row 2 INSERT INTO orders(pk=2) → 申请 Index Gap Lock → 等会话 A 或者更常见的:两会话按不同顺序访问两张表: 会话 A: UPDATE table_A(row 1) → 持有 A[1]的锁 → 等待 INSERT INTO table_B → 需要 B[间隙锁] 会话 B: UPDATE table_B(row 1) → 持有 B[1]的锁 → 等待 INSERT INTO table_A → 需要 A[间隙锁]等待图:A 等待 B → B 等待 A → 环路 → 死锁
-
Insert Intention Gap Lock:
- Insert Intention 是一种特殊的间隙锁,表示"我准备在这个间隙插入一条记录"
- 多条 INSERT 即使目标间隙重叠(如都准备插入 (10, 15) 内的不同值),它们的 Insert Intention 锁也是互相兼容的
- 这是为了在高并发 INSERT 场景下减少不必要的阻塞
- 但如果另一事务已经在该间隙上加了 Gap Lock(来自 UPDATE/DELETE),新的 INSERT 就会等待
-
减少死锁的方案:
- 方案 1:统一执行顺序。所有会话严格按照相同的顺序访问资源(如先 INSERT 再 UPDATE,或按 primary key 排序后批量更新)
- 方案 2:缩小事务范围。将 INSERT 和 UPDATE 分别放在独立的事务中执行,缩短持有锁的时间
- 方案 3:降低隔离级别到 RC。GC 隔离级别关闭 Gap Lock,INSERT 不会产生间隙锁,减少死锁概率(需评估业务对幻读的容忍度)
- 方案 4:使用唯一索引做精确更新。如果能通过唯一标识符直接定位到记录,可避免 Gap Lock
- 方案 5:批量插入代替逐条插入。合并多个 INSERT 为一个语句,减少锁竞争
评分标准:死锁场景分析(3 分)+ Insert Intention 解释(2 分)+ 至少提出 3 种可行方案(5 分)= 满分 10 分。