Skip to content

记录锁 (Record Lock) ​

📋 概述 ​

记录锁(Record Lock)是 MySQL InnoDB 引擎中最基础的锁算法,它锁定的是索引记录本身。记录锁是行级锁的核心实现方式,通过锁定聚簇索引或二级索引的记录来实现对数据行的保护。

核心特点 ​

✅ 优点:

  • 精确锁定: 只锁定需要的索引记录
  • 高并发: 不同记录可以并行访问
  • 基础性强: 其他锁算法的基础
  • 实现成熟: InnoDB 核心功能,稳定可靠

❌ 缺点:

  • 依赖索引: 必须通过索引访问才能生效
  • 单记录限制: 无法防止幻读
  • 开销较大: 每个锁记录都需要维护
  • 可能升级: 大量记录锁可能升级为表锁

适用场景 ​

  • 主键查询: 基于主键的精确查询和更新
  • 唯一索引查询: 基于唯一索引的操作
  • 单行更新: UPDATE/DELETE 单行记录
  • 点查询: WHERE id = ? 类型的查询
  • 所有行级锁场景: 记录锁是行级锁的基础

🔧 工作原理 ​

基于聚簇索引的锁定 ​

InnoDB 的数据存储在聚簇索引中,记录锁实际上是锁定聚簇索引的记录:

聚簇索引结构:
┌─────────────────┐
│ 主键值: 1       │ ← 锁定这条索引记录
│ 数据: {...}     │
│ 下一个指针: ... │
└─────────────────┘
sql
-- 基于主键的查询会锁定聚簇索引记录
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 锁定主键值为 1 的聚簇索引记录

基于二级索引的锁定 ​

如果查询使用二级索引,会同时锁定二级索引记录和对应的聚簇索引记录:

sql
-- 假设 email 是二级索引
SELECT * FROM users WHERE email = 'john@example.com' FOR UPDATE;

锁定过程:

  1. 在二级索引上找到 email = 'john@example.com' 的记录并加锁
  2. 通过二级索引记录的主键值,找到对应的聚簇索引记录
  3. 在聚簇索引记录上也加锁

结果: 两个索引记录都被锁定

锁的模式 ​

记录锁可以是共享锁或排他锁:

1. 记录共享锁 (Record S Lock) ​

sql
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- 在 id=1 的聚簇索引记录上加 S 锁

2. 记录排他锁 (Record X Lock) ​

sql
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 在 id=1 的聚簇索引记录上加 X 锁

UPDATE users SET name = 'test' WHERE id = 1;
-- 自动在 id=1 的聚簇索引记录上加 X 锁

💻 MySQL InnoDB 实现 ​

基本用法 ​

sql
-- 主键查询加锁
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 记录锁
UPDATE users SET name = 'test' WHERE id = 1;
COMMIT;

-- 二级索引查询加锁
SELECT * FROM users WHERE email = 'john@example.com' FOR UPDATE;
-- 锁定二级索引记录 + 聚簇索引记录

-- 多行记录锁
SELECT * FROM users WHERE id IN (1, 2, 3) FOR UPDATE;
-- 锁定 3 条记录

查看记录锁 ​

sql
-- 查看当前的记录锁
SELECT 
    lock_id,
    lock_trx_id,
    lock_mode,
    lock_type,
    lock_table,
    lock_index,
    lock_space,
    lock_page,
    lock_rec,
    lock_data
FROM performance_schema.data_locks
WHERE lock_type = 'RECORD';

输出示例:

lock_id   | lock_mode | lock_index | lock_data
----------|-----------|------------|----------
12345:100 | X         | PRIMARY    | 1

解读:

  • lock_mode = X: 排他锁
  • lock_index = PRIMARY: 聚簇索引
  • lock_data = 1: 主键值为 1 的记录

唯一索引 vs 普通索引 ​

唯一索引的记录锁 ​

sql
-- 假设 username 是唯一索引
SELECT * FROM users WHERE username = 'john' FOR UPDATE;

锁定行为:

  • 只锁定匹配的唯一索引记录
  • 不会锁定间隙
  • 其他事务可以插入新的 username

普通索引的记录锁 ​

sql
-- 假设 age 是普通索引(非唯一)
SELECT * FROM users WHERE age = 18 FOR UPDATE;

锁定行为:

  • 锁定所有 age=18 的索引记录
  • 在 REPEATABLE READ 级别下,还会锁定间隙(Next-Key Lock)
  • 防止其他事务插入 age=18 的新记录

📊 性能影响分析 ​

并发度评估 ​

场景并发度说明
不同记录的查询⭐⭐⭐⭐⭐完全并行
同一记录的查询⭐串行执行
范围查询⭐⭐⭐锁定多记录
无索引查询⭐退化为表锁

性能测试 ​

sql
-- 测试场景:100个并发事务更新不同记录

-- 使用记录锁
-- 100个事务并行执行,总耗时:~1秒
BEGIN;
SELECT * FROM users WHERE id = ? FOR UPDATE;  -- 不同的 id
UPDATE users SET balance = balance - 100 WHERE id = ?;
COMMIT;

-- 更新同一记录
-- 100个事务串行执行,总耗时:~50秒
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 相同的 id
UPDATE users SET balance = balance - 100 WHERE id = 1;
COMMIT;

🎯 最佳实践 ​

✅ 推荐做法 ​

1. 确保查询使用索引 ​

sql
-- ✅ 添加索引
CREATE INDEX idx_email ON users(email);

-- 使用索引查询
SELECT * FROM users WHERE email = 'john@example.com' FOR UPDATE;

2. 精确锁定需要的记录 ​

sql
-- ✅ 使用主键精确定位
SELECT * FROM users WHERE id = 1 FOR UPDATE;

-- ❌ 避免范围过大
SELECT * FROM users WHERE age > 18 FOR UPDATE;  -- 锁定太多记录

3. 批量操作优化 ​

sql
-- ❌ 逐行处理
FOR i IN 1..1000 LOOP
    SELECT * FROM users WHERE id = i FOR UPDATE;
    UPDATE users SET ... WHERE id = i;
END LOOP;

-- ✅ 批量处理
SELECT * FROM users WHERE id BETWEEN 1 AND 1000 FOR UPDATE;
UPDATE users SET ... WHERE id BETWEEN 1 AND 1000;

❌ 避免陷阱 ​

1. 避免无索引的记录锁 ​

sql
-- ❌ 危险:全表扫描,退化为表锁
SELECT * FROM users WHERE age > 18 FOR UPDATE;  -- age 无索引

-- ✅ 添加索引
CREATE INDEX idx_age ON users(age);

2. 避免锁定过多记录 ​

sql
-- ❌ 不推荐:锁定整个表
SELECT * FROM users FOR UPDATE;

-- ✅ 推荐:只锁定需要的记录
SELECT * FROM users WHERE id = 1 FOR UPDATE;

🔍 监控与诊断 ​

sql
-- 查看记录锁
SELECT 
    lock_id,
    lock_mode,
    lock_index,
    lock_data,
    trx.trx_id,
    trx.trx_state,
    trx.trx_started
FROM performance_schema.data_locks locks
JOIN information_schema.innodb_trx trx ON locks.lock_trx_id = trx.trx_id
WHERE locks.lock_type = 'RECORD'
ORDER BY trx.trx_started;

-- 查看记录锁等待
SELECT * FROM performance_schema.data_lock_waits;

🚨 常见问题 ​

Q1: 记录锁和行级锁的关系? ​

答: 记录锁是行级锁的实现方式。行级锁是概念,记录锁是具体算法。


Q2: 记录锁能防止幻读吗? ​

答: 不能。需要配合间隙锁(Gap Lock)或临键锁(Next-Key Lock)才能防止幻读。


📚 相关资源 ​

内部链接 ​

外部资源 ​


最后更新: 2026-04-12
维护状态: ✅ 完整

Released under MIT License.