Skip to content

意向锁 (Intention Lock) ​

📋 概述 ​

意向锁(Intention Lock)是一种表级锁,用于表明事务打算在表中的行级别获取更细粒度的锁。意向锁本身不锁定任何实际数据,而是作为一种"信号"或"意图声明",帮助数据库快速判断表上是否存在行级锁。

核心特点 ​

✅ 优点:

  • 快速判断: 无需扫描所有行即可知道表上是否有行锁
  • 锁协调: 协调表级锁和行级锁的关系
  • 提高并发: 允许多层级的锁共存
  • 减少开销: 避免全表扫描检查行锁

❌ 缺点:

  • 额外开销: 需要维护意向锁信息
  • 理解复杂: 概念相对抽象
  • 仅 InnoDB: MySQL 中只有 InnoDB 支持

适用场景 ​

  • InnoDB 引擎: MySQL InnoDB 自动使用
  • 混合锁粒度: 同时使用表锁和行锁的场景
  • 高并发系统: 需要快速判断锁冲突
  • DDL 与 DML 共存: 表结构修改和数据操作同时进行

🔧 工作原理 ​

意向锁的类型 ​

1. 意向共享锁 (IS - Intention Shared) ​

表明事务打算在行上加共享锁(S锁)。

事务请求: 在行上加 S 锁
    ↓
先在表上加 IS 锁
    ↓
然后在行上加 S 锁

兼容性:

  • 与其他 IS 锁兼容 ✅
  • 与 IX 锁兼容 ✅
  • 与 S 锁兼容 ✅
  • 与 X 锁不兼容 ❌

2. 意向排他锁 (IX - Intention Exclusive) ​

表明事务打算在行上加排他锁(X锁)。

事务请求: 在行上加 X 锁
    ↓
先在表上加 IX 锁
    ↓
然后在行上加 X 锁

兼容性:

  • 与其他 IX 锁兼容 ✅
  • 与 IS 锁兼容 ✅
  • 与 S 锁不兼容 ❌
  • 与 X 锁不兼容 ❌

锁兼容性矩阵 ​

        | IS    | IX    | S     | X
--------|-------|-------|-------|------
IS      | ✅    | ✅    | ✅    | ❌
IX      | ✅    | ✅    | ❌    | ❌
S       | ✅    | ❌    | ✅    | ❌
X       | ❌    | ❌    | ❌    | ❌

解读示例:

  • IS 与 IX 兼容 → 一个事务可以在表上加 IS 锁,另一个事务可以加 IX 锁
  • S 与 IX 不兼容 → 如果表上有 IX 锁,不能再加 S 锁(表级)

工作流程示例 ​

sql
-- 事务 A
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 需要在行上加 X 锁
-- 步骤1: 在 users 表上加 IX 锁
-- 步骤2: 在 id=1 的行上加 X 锁

-- 事务 B(同时)
BEGIN;
SELECT * FROM users WHERE id = 2 FOR UPDATE;  -- 也需要在行上加 X 锁
-- 步骤1: 尝试在 users 表上加 IX 锁 → ✅ 成功(IX 与 IX 兼容)
-- 步骤2: 在 id=2 的行上加 X 锁 → ✅ 成功(不同行)

-- 事务 C(同时)
LOCK TABLES users READ;  -- 需要在表上加 S 锁
-- 检查表上是否有 IX 锁 → ❌ 发现事务 A 和 B 有 IX 锁
-- 等待事务 A 和 B 提交

关键点:

  • 如果没有意向锁,事务 C 需要扫描所有行来检查是否有行锁
  • 有了意向锁,只需检查表级的意向锁即可

💻 各数据库实现 ​

MySQL (InnoDB) ​

InnoDB 自动管理意向锁,用户无需显式指定。

自动添加意向锁 ​

sql
-- 事务 A
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- InnoDB 自动执行:
-- 1. 在 users 表上加 IX 锁
-- 2. 在 id=1 的行上加 X 锁

-- 事务 B
BEGIN;
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
-- InnoDB 自动执行:
-- 1. 在 users 表上加 IS 锁
-- 2. 在 id=1 的行上加 S 锁
COMMIT;

查看意向锁 ​

sql
-- 查看表级意向锁
SELECT 
    lock_id,
    lock_trx_id,
    lock_mode,
    lock_type,
    lock_table
FROM performance_schema.data_locks
WHERE lock_type = 'TABLE'
  AND lock_mode IN ('IS', 'IX');

-- 完整的锁层次结构
SELECT 
    lock_id,
    lock_trx_id,
    lock_mode,
    lock_type,
    lock_table,
    lock_index,
    lock_data
FROM performance_schema.data_locks
ORDER BY lock_type, lock_table;

输出示例:

lock_id      | lock_mode | lock_type | lock_table | lock_data
-------------|-----------|-----------|------------|----------
12345:100    | IX        | TABLE     | users      | NULL
12345:100:1  | X         | RECORD    | users      | 1

解读:

  • 第一行:事务 12345 在 users 表上有 IX 锁
  • 第二行:事务 12345 在 users 表的 id=1 记录上有 X 锁

DDL 操作与意向锁 ​

sql
-- 事务 A
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 表上有 IX 锁

-- 事务 B
ALTER TABLE users ADD COLUMN age INT;  -- 需要在表上加 X 锁
-- ❌ 被阻塞,因为 IX 与 X 不兼容
-- 等待事务 A 提交

工作流程:

  1. ALTER TABLE 需要在表上加 X 锁
  2. 检查表上是否有 IS/IX 锁
  3. 发现有 IX 锁,等待其释放
  4. 事务 A 提交后,IX 锁释放
  5. ALTER TABLE 获得 X 锁并执行

SQL Server ​

SQL Server 也使用意向锁,原理类似。

查看意向锁 ​

sql
-- 查看意向锁
SELECT 
    request_session_id,
    resource_type,
    resource_database_id,
    resource_associated_entity_id,
    request_mode,
    request_status
FROM sys.dm_tran_locks
WHERE request_mode IN ('IS', 'IX');

-- 查看完整的锁层次
SELECT 
    tl.request_session_id,
    OBJECT_NAME(tl.resource_associated_entity_id) AS table_name,
    tl.resource_type,
    tl.request_mode,
    tl.request_status
FROM sys.dm_tran_locks tl
WHERE tl.resource_type IN ('OBJECT', 'PAGE', 'KEY')
ORDER BY tl.resource_type;

输出示例:

session_id | table_name | resource_type | request_mode
-----------|------------|---------------|-------------
51         | users      | OBJECT        | IX
51         | users      | PAGE          | IX
51         | users      | KEY           | X

解读:

  • 会话 51 在 users 表对象上有 IX 锁
  • 在某个页上有 IX 锁
  • 在某条记录(KEY)上有 X 锁

📊 性能影响分析 ​

开销评估 ​

操作无意向锁有意向锁
检查表上是否有行锁O(N) 扫描所有行O(1) 检查表锁
DDL 阻塞判断慢快
锁管理开销低略高

并发度提升 ​

场景:100个事务在不同行上加锁,1个 DDL 操作

无意向锁:
- DDL 需要扫描 100 行检查锁
- 耗时:~1秒

有意向锁:
- DDL 只需检查表级意向锁
- 耗时:~0.001秒

性能提升:1000倍

🎯 最佳实践 ​

✅ 推荐做法 ​

1. 让数据库自动管理 ​

sql
-- ✅ 推荐:无需关心意向锁,InnoDB 自动处理
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
UPDATE users SET name = 'test' WHERE id = 1;
COMMIT;

-- ❌ 不需要(也无法)显式指定意向锁
-- LOCK TABLES users IN INTENTION EXCLUSIVE MODE;  -- 语法错误

2. 监控意向锁等待 ​

sql
-- 监控长时间的意向锁等待
SELECT 
    l.lock_id,
    l.lock_trx_id,
    l.lock_mode,
    l.lock_table,
    t.trx_started,
    TIMESTAMPDIFF(SECOND, t.trx_started, NOW()) AS duration
FROM performance_schema.data_locks l
JOIN information_schema.innodb_trx t ON l.lock_trx_id = t.trx_id
WHERE l.lock_type = 'TABLE'
  AND l.lock_mode IN ('IS', 'IX')
  AND TIMESTAMPDIFF(SECOND, t.trx_started, NOW()) > 10;

3. 理解 DDL 阻塞原因 ​

sql
-- 如果 DDL 被阻塞,检查是否有长事务持有 IX 锁
SELECT 
    t.trx_id,
    t.trx_started,
    t.trx_state,
    t.trx_mysql_thread_id,
    p.info
FROM information_schema.innodb_trx t
JOIN information_schema.processlist p ON t.trx_mysql_thread_id = p.id
WHERE t.trx_state = 'RUNNING'
ORDER BY t.trx_started;

❌ 避免陷阱 ​

1. 避免长事务持有 IX 锁 ​

sql
-- ❌ 不推荐:长时间持有 IX 锁,阻塞 DDL
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- 执行大量无关操作(60秒)
SLEEP(60);
COMMIT;

-- ✅ 推荐:快速完成
BEGIN;
SELECT * FROM users WHERE id = 1 FOR UPDATE;
UPDATE users SET name = 'test' WHERE id = 1;
COMMIT;

2. 避免忽略意向锁的影响 ​

sql
-- 事务 A
BEGIN;
UPDATE users SET name = 'test' WHERE id = 1;  -- 表上有 IX 锁

-- 事务 B
ALTER TABLE users ADD COLUMN age INT;  -- 被阻塞!
-- 即使操作的是不同的行,DDL 仍需等待

-- 解决:等待事务 A 提交,或在业务低峰期执行 DDL

🔍 监控与诊断 ​

MySQL 监控 ​

sql
-- 查看所有意向锁
SELECT 
    lock_id,
    lock_trx_id,
    lock_mode,
    lock_type,
    lock_table
FROM performance_schema.data_locks
WHERE lock_type = 'TABLE'
  AND lock_mode IN ('IS', 'IX');

-- 查看意向锁导致的阻塞
SELECT 
    requesting_trx_id,
    blocking_trx_id
FROM performance_schema.data_lock_waits;

-- 综合视图
SELECT 
    l.lock_mode,
    l.lock_type,
    l.lock_table,
    t.trx_id,
    t.trx_state,
    t.trx_started,
    p.info AS current_query
FROM performance_schema.data_locks l
LEFT JOIN information_schema.innodb_trx t ON l.lock_trx_id = t.trx_id
LEFT JOIN information_schema.processlist p ON t.trx_mysql_thread_id = p.id
WHERE l.lock_type = 'TABLE'
ORDER BY t.trx_started;

🚨 常见问题 ​

Q1: 为什么需要意向锁? ​

答:

没有意向锁的问题:

DDL 操作需要检查表上是否有行锁:
→ 必须扫描所有行
→ 性能极差 O(N)

有意向锁的优势:

DDL 操作只需检查表级意向锁:
→ 直接查看表锁状态
→ 性能极高 O(1)

Q2: 用户可以显式指定意向锁吗? ​

答:

MySQL: 不可以。意向锁由 InnoDB 自动管理。

sql
-- ❌ 错误:无法显式指定
LOCK TABLES users IN INTENTION EXCLUSIVE MODE;

-- ✅ 正确:让 InnoDB 自动处理
SELECT * FROM users WHERE id = 1 FOR UPDATE;
-- InnoDB 自动添加 IX 锁

SQL Server: 同样自动管理,但可以通过提示影响锁行为。


Q3: 意向锁会导致死锁吗? ​

答:

意向锁本身不会导致死锁,因为它们都是表级锁且互相兼容(IS 与 IX 兼容)。

死锁通常发生在行级别:

sql
-- 事务 A
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 行1的 X 锁
SELECT * FROM users WHERE id = 2 FOR UPDATE;  -- 等待行2的 X 锁

-- 事务 B
SELECT * FROM users WHERE id = 2 FOR UPDATE;  -- 行2的 X 锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;  -- 等待行1的 X 锁 → 死锁

Q4: 如何优化意向锁相关的性能问题? ​

答:

  1. 缩短事务时间: 快速提交,释放 IX 锁
  2. 分批执行 DDL: 避免长时间等待
  3. 业务低峰期 DDL: 减少 IX 锁冲突
  4. 监控长事务: 及时发现和终止
sql
-- 监控长事务
SELECT * FROM information_schema.innodb_trx 
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 10;

📚 相关资源 ​

内部链接 ​

外部资源 ​


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

Released under MIT License.