Skip to content

表级锁 (Table-Level Lock) ​

📋 概述 ​

表级锁是数据库锁机制中粒度较大的一种锁类型,它锁定的是整张数据表。当一个事务获得某张表的表级锁后,其他事务在该表上的某些操作会被阻塞,直到锁被释放。

核心特点 ​

✅ 优点:

  • 开销小: 只需要维护一个锁对象,内存占用少
  • 加锁快: 无需扫描索引或数据行,加锁速度快
  • 无死锁: 一次性获取所有需要的锁,不会出现循环等待
  • 实现简单: 数据库引擎实现复杂度低

❌ 缺点:

  • 并发度低: 锁冲突概率高,大量事务需要等待
  • 灵活性差: 即使只操作一行数据,也会锁定整张表
  • 扩展性差: 不适合高并发的 OLTP 场景

适用场景 ​

  • DDL 操作: ALTER TABLE, CREATE INDEX, DROP TABLE 等
  • 批量操作: 大批量数据导入、导出
  • 全表扫描: 需要对整张表进行一致性读取
  • 维护操作: 表优化、统计分析
  • 低并发系统: 读多写少的应用场景

🔧 工作原理 ​

锁的获取与释放 ​

sql
-- 显式获取表级锁
LOCK TABLES table_name [READ | WRITE];

-- 执行操作
SELECT * FROM table_name;

-- 释放锁(事务提交或回滚时自动释放)
UNLOCK TABLES;

锁的模式 ​

表级锁主要有两种模式:

1. 共享读锁 (READ / SHARE) ​

  • 多个事务可以同时获得同一张表的读锁
  • 持有读锁的事务可以读取表数据
  • 阻止其他事务获得写锁
  • 阻止其他事务修改数据
sql
-- 事务 A
LOCK TABLES users READ;
SELECT * FROM users;  -- ✅ 允许

-- 事务 B(同时)
LOCK TABLES users READ;
SELECT * FROM users;  -- ✅ 允许,共享锁兼容

-- 事务 C
UPDATE users SET name = 'test' WHERE id = 1;  -- ❌ 阻塞,等待写锁

2. 排他写锁 (WRITE / EXCLUSIVE) ​

  • 只有一个事务可以获得某张表的写锁
  • 持有写锁的事务可以读写表数据
  • 阻止其他事务获得读锁或写锁
  • 阻止其他事务读写数据
sql
-- 事务 A
LOCK TABLES users WRITE;
UPDATE users SET name = 'test' WHERE id = 1;  -- ✅ 允许

-- 事务 B(同时)
SELECT * FROM users;  -- ❌ 阻塞,等待读锁
UPDATE users SET name = 'test2' WHERE id = 2;  -- ❌ 阻塞

隐式表级锁 ​

除了显式的 LOCK TABLES,很多操作会自动获取表级锁:

操作类型锁模式说明
ALTER TABLEWRITE修改表结构需要独占访问
DROP TABLEWRITE删除表需要独占访问
CREATE INDEXREAD/WRITE取决于数据库实现
TRUNCATE TABLEWRITE清空表需要独占访问
LOCK TABLES ... READREAD显式指定
LOCK TABLES ... WRITEWRITE显式指定

💻 各数据库实现 ​

MySQL ​

MyISAM 引擎(默认表级锁) ​

MyISAM 引擎只支持表级锁,不支持行级锁:

sql
-- 查看表的存储引擎
SHOW TABLE STATUS LIKE 'users';

-- MyISAM 表的所有操作都是表级锁
-- SELECT 自动加读锁
SELECT * FROM users;

-- INSERT/UPDATE/DELETE 自动加写锁
UPDATE users SET name = 'test' WHERE id = 1;

特点:

  • 读操作并发执行(共享读锁)
  • 写操作独占(排他写锁)
  • 写操作会阻塞所有读操作
  • 适合读多写少的场景

InnoDB 引擎(支持表级锁和行级锁) ​

InnoDB 主要使用行级锁,但也支持表级锁:

sql
-- 显式锁定表
LOCK TABLES users READ, orders WRITE;

-- 执行操作
SELECT * FROM users;
INSERT INTO orders VALUES (1, 100);

-- 解锁
UNLOCK TABLES;

意向锁机制:

InnoDB 使用意向锁来协调表级锁和行级锁:

事务在行上加 S 锁 → 先在表上加 IS(意向共享锁)
事务在行上加 X 锁 → 先在表上加 IX(意向排他锁)

这样可以快速判断表上是否有行级锁,无需扫描所有行。

sql
-- 查看 InnoDB 锁信息
SELECT * FROM performance_schema.data_locks 
WHERE lock_type = 'TABLE';

参数配置:

ini
# my.cnf
# 表锁等待超时时间(秒)
innodb_lock_wait_timeout = 50

# 是否启用表锁监控
performance_schema = ON

PostgreSQL ​

PostgreSQL 提供了多种表级锁模式:

sql
-- 显式锁定表
LOCK TABLE users IN ACCESS SHARE MODE;     -- 最弱,与除 ACCESS EXCLUSIVE 外的所有锁兼容
LOCK TABLE users IN ROW SHARE MODE;        -- SELECT FOR UPDATE/FOR SHARE 自动获取
LOCK TABLE users IN ROW EXCLUSIVE MODE;    -- INSERT/UPDATE/DELETE 自动获取
LOCK TABLE users IN SHARE UPDATE EXCLUSIVE MODE;  -- ANALYZE 等操作
LOCK TABLE users IN SHARE MODE;            -- CREATE INDEX 自动获取
LOCK TABLE users IN SHARE ROW EXCLUSIVE MODE;     -- 比 SHARE 更强
LOCK TABLE users IN EXCLUSIVE MODE;        -- 阻止所有其他操作
LOCK TABLE users IN ACCESS EXCLUSIVE MODE; -- 最强,ALTER TABLE/DROP TABLE 自动获取

锁兼容性矩阵:

请求锁模式          | ACCESS | ROW | ROW EXCL | SHARE UPDATE | SHARE | SHARE ROW | EXCL | ACCESS EXCL
                    | SHARE  | SHARE|          | EXCL         |       | EXCL      |      | EXCL
--------------------|--------|-----|----------|--------------|-------|-----------|------|------------
ACCESS SHARE        |   ✓    |  ✓  |    ✓     |      ✓       |   ✓   |     ✓     |  ✓   |    ✗
ROW SHARE           |   ✓    |  ✓  |    ✓     |      ✓       |   ✓   |     ✓     |  ✗   |    ✗
ROW EXCLUSIVE       |   ✓    |  ✓  |    ✓     |      ✗       |   ✗   |     ✗     |  ✗   |    ✗
SHARE UPDATE EXCL   |   ✓    |  ✓  |    ✗     |      ✗       |   ✗   |     ✗     |  ✗   |    ✗
SHARE               |   ✓    |  ✓  |    ✗     |      ✗       |   ✓   |     ✗     |  ✗   |    ✗
SHARE ROW EXCLUSIVE |   ✓    |  ✗  |    ✗     |      ✗       |   ✗   |     ✗     |  ✗   |    ✗
EXCLUSIVE           |   ✓    |  ✗  |    ✗     |      ✗       |   ✗   |     ✗     |  ✗   |    ✗
ACCESS EXCLUSIVE    |   ✗    |  ✗  |    ✗     |      ✗       |   ✗   |     ✗     |  ✗   |    ✗

查看锁信息:

sql
-- 查看当前所有锁
SELECT 
    l.locktype,
    l.relation::regclass AS table_name,
    l.mode,
    l.granted,
    a.pid,
    a.query,
    a.usename
FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
WHERE l.relation IS NOT NULL
ORDER BY l.relation;

-- 查看锁等待
SELECT 
    blocked_locks.pid AS blocked_pid,
    blocked_activity.usename AS blocked_user,
    blocking_locks.pid AS blocking_pid,
    blocking_activity.usename AS blocking_user,
    blocked_activity.query AS blocked_statement,
    blocking_activity.query AS current_statement_in_blocking_process
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks 
    ON blocking_locks.locktype = blocked_locks.locktype
    AND blocking_locks.relation = blocked_locks.relation
    AND blocking_locks.granted
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

SQL Server ​

SQL Server 支持表级锁,并且有锁升级机制:

sql
-- 显式锁定表
SELECT * FROM users WITH (TABLOCK);           -- 共享表锁
SELECT * FROM users WITH (TABLOCKX);          -- 排他表锁

-- 在事务中使用
BEGIN TRANSACTION;
    SELECT * FROM orders WITH (HOLDLOCK, TABLOCK);
    -- 执行其他操作
COMMIT TRANSACTION;

-- 禁用锁升级(针对特定表)
ALTER TABLE users SET (LOCK_ESCALATION = DISABLE);

锁升级机制:

当行锁数量超过阈值时,SQL Server 会自动升级为表锁:

行锁数量 > 5000 或 内存阈值
    ↓
升级为表级锁

配置锁升级:

sql
-- 查看锁升级配置
SELECT name, lock_escalation_desc 
FROM sys.tables;

-- 修改锁升级策略
ALTER TABLE users SET (LOCK_ESCALATION = AUTO);    -- 自动(默认)
ALTER TABLE users SET (LOCK_ESCALATION = TABLE);   -- 总是升级到表锁
ALTER TABLE users SET (LOCK_ESCALATION = DISABLE); -- 禁用升级

-- 设置升级阈值
ALTER TABLE users SET (LOCK_ESCALATION_THRESHOLD = 1000);

查看锁信息:

sql
-- 查看当前锁
SELECT 
    request_session_id,
    resource_type,
    resource_database_id,
    resource_associated_entity_id,
    request_mode,
    request_status
FROM sys.dm_tran_locks
WHERE resource_type = 'OBJECT';

-- 查看具体的表锁
SELECT 
    tl.request_session_id,
    OBJECT_NAME(tl.resource_associated_entity_id) AS table_name,
    tl.request_mode,
    tl.request_status,
    es.host_name,
    es.program_name,
    es.login_name
FROM sys.dm_tran_locks tl
JOIN sys.dm_exec_sessions es ON tl.request_session_id = es.session_id
WHERE tl.resource_type = 'OBJECT';

Oracle ​

Oracle 中的表级锁主要通过 LOCK TABLE 语句实现:

sql
-- 显式锁定表
LOCK TABLE users IN ROW SHARE MODE;           -- 共享行锁(允许并发更新其他行)
LOCK TABLE users IN ROW EXCLUSIVE MODE;       -- 排他行锁(默认,DML自动获取)
LOCK TABLE users IN SHARE MODE;               -- 共享锁(阻止其他事务获取排他锁)
LOCK TABLE users IN SHARE ROW EXCLUSIVE MODE; -- 共享行排他锁
LOCK TABLE users IN EXCLUSIVE MODE;           -- 排他锁(最强)

-- 指定等待行为
LOCK TABLE users IN EXCLUSIVE MODE NOWAIT;    -- 无法立即获取则报错
LOCK TABLE users IN EXCLUSIVE MODE WAIT 10;   -- 等待10秒

特点:

  • Oracle 大量使用 MVCC,读写不阻塞
  • 表级锁主要用于 DDL 操作和特殊场景
  • DML 操作自动获取 ROW EXCLUSIVE 模式锁
  • 支持 NOWAIT 和 WAIT 选项

查看锁信息:

sql
-- 查看表锁
SELECT 
    s.sid,
    s.serial#,
    s.username,
    l.type,
    l.lmode,
    l.request,
    o.object_name,
    s.lockwait
FROM v$session s
JOIN v$lock l ON s.sid = l.sid
JOIN dba_objects o ON l.id1 = o.object_id
WHERE l.type = 'TM'  -- TM = DML/DDL 锁
ORDER BY o.object_name;

-- 查看锁等待
SELECT 
    waiting_session,
    holding_session,
    lock_type,
    mode_held,
    mode_requested
FROM dba_waiters;

📊 性能影响分析 ​

并发度评估 ​

锁模式读并发写并发适用场景
READ (共享)⭐⭐⭐⭐⭐⭐读多写少
WRITE (排他)⭐⭐批量写入

开销对比 ​

指标表级锁行级锁
内存占用低(1个锁对象)高(N个锁对象)
加锁速度快慢
锁冲突概率高低
并发度低高
死锁风险无有

性能测试示例 ​

sql
-- 测试表级锁的性能影响
-- 场景:100个并发事务更新不同行

-- 使用表级锁
LOCK TABLES users WRITE;
-- 100个事务串行执行,总耗时:~50秒

-- 使用行级锁(InnoDB)
-- 100个事务并行执行,总耗时:~1秒

🎯 最佳实践 ​

✅ 推荐做法 ​

1. 优先使用行级锁 ​

sql
-- ❌ 不推荐:不必要的表级锁
LOCK TABLES users WRITE;
UPDATE users SET name = 'test' WHERE id = 1;
UNLOCK TABLES;

-- ✅ 推荐:让数据库自动管理行级锁
BEGIN;
UPDATE users SET name = 'test' WHERE id = 1;
COMMIT;

2. 缩短锁持有时间 ​

sql
-- ❌ 不推荐:长时间持有表锁
LOCK TABLES users WRITE;
SELECT * FROM users;  -- 大量数据处理
UPDATE users SET ...;
UNLOCK TABLES;

-- ✅ 推荐:快速完成操作
BEGIN;
UPDATE users SET ... WHERE id = 1;  -- 快速操作
COMMIT;  -- 立即释放锁

3. 批量操作时使用表锁 ​

sql
-- ✅ 推荐:大批量导入时使用表锁提高性能
LOCK TABLES users WRITE;
LOAD DATA INFILE '/path/to/data.csv' INTO TABLE users;
UNLOCK TABLES;

4. 合理设置超时 ​

sql
-- MySQL
SET innodb_lock_wait_timeout = 10;  -- 10秒超时

-- PostgreSQL
SET lock_timeout = '5s';  -- 5秒超时

-- SQL Server
SET LOCK_TIMEOUT 5000;  -- 5000毫秒超时

❌ 避免陷阱 ​

1. 避免在循环中使用表锁 ​

sql
-- ❌ 错误示例
FOR i IN 1..1000 LOOP
    LOCK TABLES users WRITE;
    UPDATE users SET ... WHERE id = i;
    UNLOCK TABLES;
END LOOP;

-- ✅ 正确示例
BEGIN;
FOR i IN 1..1000 LOOP
    UPDATE users SET ... WHERE id = i;
END LOOP;
COMMIT;

2. 避免不一致的加锁顺序 ​

sql
-- ❌ 可能导致死锁
-- 事务 A
LOCK TABLES users WRITE, orders WRITE;

-- 事务 B
LOCK TABLES orders WRITE, users WRITE;

-- ✅ 固定顺序
-- 事务 A 和 B 都按相同顺序加锁
LOCK TABLES users WRITE, orders WRITE;

3. 避免忘记解锁 ​

sql
-- ❌ 危险:异常时可能未解锁
LOCK TABLES users WRITE;
UPDATE users SET ...;  -- 如果这里出错
UNLOCK TABLES;  -- 不会执行

-- ✅ 使用事务确保解锁
BEGIN;
LOCK TABLES users WRITE;
UPDATE users SET ...;
COMMIT;  -- 自动解锁

4. 避免在高并发系统使用表锁 ​

sql
-- ❌ OLTP 系统避免表锁
LOCK TABLES users WRITE;
SELECT * FROM users WHERE id = 1;

-- ✅ 使用行锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;

🔍 监控与诊断 ​

MySQL 监控 ​

sql
-- 查看表锁等待
SELECT 
    request_owner_thread_id,
    object_schema,
    object_name,
    lock_type,
    lock_mode,
    lock_status
FROM performance_schema.metadata_locks
WHERE lock_status = 'PENDING';

-- 查看 InnoDB 表锁
SELECT * FROM performance_schema.data_locks 
WHERE lock_type = 'TABLE';

-- 查看锁等待详情
SHOW ENGINE INNODB STATUS\G

PostgreSQL 监控 ​

sql
-- 查看长时间持有的表锁
SELECT 
    l.relation::regclass AS table_name,
    l.mode,
    now() - a.query_start AS duration,
    a.query,
    a.pid
FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
WHERE l.granted 
  AND l.relation IS NOT NULL
  AND now() - a.query_start > interval '1 minute'
ORDER BY duration DESC;

SQL Server 监控 ​

sql
-- 查看阻塞链
WITH BlockingChain AS (
    SELECT 
        session_id,
        blocking_session_id,
        wait_time,
        wait_type,
        1 AS level
    FROM sys.dm_exec_requests
    WHERE blocking_session_id <> 0
    
    UNION ALL
    
    SELECT 
        r.session_id,
        r.blocking_session_id,
        r.wait_time,
        r.wait_type,
        bc.level + 1
    FROM sys.dm_exec_requests r
    JOIN BlockingChain bc ON r.blocking_session_id = bc.session_id
)
SELECT * FROM BlockingChain
ORDER BY level, session_id;

🚨 常见问题 ​

Q1: 表级锁会导致死锁吗? ​

答: 理论上不会。因为表级锁通常是一次性获取所有需要的锁,不存在循环等待。但如果多次调用 LOCK TABLES,仍可能产生死锁。

sql
-- 可能产生死锁的场景
-- 事务 A
LOCK TABLES users WRITE;
LOCK TABLES orders WRITE;  -- 等待事务B释放

-- 事务 B
LOCK TABLES orders WRITE;
LOCK TABLES users WRITE;   -- 等待事务A释放 → 死锁

解决: 始终按固定顺序加锁。


Q2: 如何避免表锁阻塞查询? ​

答:

  1. 使用读写分离: 主库写,从库读
  2. 缩短事务时间: 快速提交
  3. 选择合适时机: 在业务低峰期执行 DDL
  4. 使用在线 DDL: MySQL 8.0+ 的 ALGORITHM=INPLACE
sql
-- MySQL 8.0+ 在线添加索引
ALTER TABLE users ADD INDEX idx_name (name), ALGORITHM=INPLACE, LOCK=NONE;

Q3: 表级锁和行级锁如何选择? ​

答:

场景推荐锁类型
高并发 OLTP行级锁
批量数据导入表级锁
DDL 操作表级锁(必须)
全表统计表级锁或快照读
读多写少表级锁可接受
写频繁必须行级锁

Q4: 如何排查表锁导致的性能问题? ​

答:

sql
-- 步骤1: 查找阻塞源
SELECT * FROM information_schema.innodb_trx 
WHERE trx_state = 'LOCK WAIT';

-- 步骤2: 查看锁详情
SELECT * FROM performance_schema.data_locks 
WHERE lock_type = 'TABLE';

-- 步骤3: 终止长时间运行的事务
KILL <process_id>;

-- 步骤4: 优化查询和事务
-- - 添加索引
-- - 拆分大事务
-- - 减少锁持有时间

📚 相关资源 ​

内部链接 ​

外部资源 ​


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

Released under MIT License.