Files
autumn-recruitment/02.MySQL/transaction/事务隔离级别与锁机制_test.md
T

213 lines
11 KiB
Markdown
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.
---
tags: [test/review, mysql, transaction-isolation, gap-lock, next-key-lock, deadlock-detection]
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;
```
请回答:
1. 如果两条 SQL 的执行顺序不一致(例如先 INSERT 再 UPDATE),什么情况下会产生死锁?画出等待图说明
2. Insert Intention Gap Lock 在 INSERT 操作中起什么作用?它为什么不会与其他 INSERT 产生冲突?
3. 给出减少此类死锁的方案设计建议
> **答题框架提示**: 分析两会话的锁依赖关系 → 识别循环等待链 → 理解 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 参考答案要点**:
1. **死锁条件与等待图**:
- 如果会话 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 → 环路 → 死锁
2. **Insert Intention Gap Lock**:
- Insert Intention 是一种特殊的间隙锁,表示"我准备在这个间隙插入一条记录"
- 多条 INSERT 即使目标间隙重叠(如都准备插入 (10, 15) 内的不同值),它们的 Insert Intention 锁也是互相**兼容**的
- 这是为了在高并发 INSERT 场景下减少不必要的阻塞
- 但如果另一事务已经在该间隙上加了 Gap Lock(来自 UPDATE/DELETE),新的 INSERT 就会等待
3. **减少死锁的方案**:
- 方案 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 分。
## 关联笔记
- [[02.MySQL/transaction/事务隔离级别与锁机制]]