行锁与表锁详解
一、定义与核心区别
-
行锁(Row Lock)
- 定义:锁定表中的单行数据,其他事务可操作表中其他行。
- 特点:锁粒度小、并发度高,但可能引发死锁
- 适用场景:高并发读写(如账户余额更新、订单处理)
-
表锁(Table Lock)
- 定义:锁定整张表,其他事务无法进行写操作。
- 特点:锁粒度大、并发度低,但实现简单且无死锁风险
- 适用场景:批量操作(如ALTER TABLE、全表更新)或低并发场景
二、触发条件
-
行锁触发条件
- 索引条件:查询或更新语句必须使用索引(主键或二级索引),否则退化为表锁
- 事务操作:在事务中对数据行执行
SELECT ... FOR UPDATE
或UPDATE/DELETE
语句 - 引擎限制:仅InnoDB引擎支持行锁,MyISAM仅支持表锁
示例代码(行锁):
-- 事务中通过主键加行锁 BEGIN; SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 行级排他锁 UPDATE users SET balance = balance - 100 WHERE id = 1; COMMIT;
-
表锁触发条件
- 无索引操作:当WHERE条件未命中索引时,InnoDB自动升级为表锁
- 显式锁定:使用
LOCK TABLES
命令手动加锁 - DDL操作:ALTER TABLE、TRUNCATE等结构变更操作自动加表锁
示例代码(表锁):
-- 显式锁定整张表 LOCK TABLES users WRITE; -- 执行批量更新 UPDATE users SET status = 'inactive' WHERE create_time < '2024-01-01'; UNLOCK TABLES;
三、关键注意事项
-
索引的重要性
- 若未使用索引,即使InnoDB引擎也会退化为表锁。例如:
-- name字段无索引时,触发表锁 UPDATE users SET balance = 0 WHERE name = 'Alice'; -- 全表扫描,表级锁[8,10](@ref)
- 若未使用索引,即使InnoDB引擎也会退化为表锁。例如:
-
死锁风险
- 行锁可能因事务交叉等待导致死锁,需通过
SHOW ENGINE INNODB STATUS
分析 - 优化建议:事务按固定顺序操作资源,减少锁持有时间
- 行锁可能因事务交叉等待导致死锁,需通过
-
锁类型与隔离级别
- 可重复读(RR)隔离级别下,InnoDB会使用间隙锁(Gap Lock)防止幻读
- 示例:
-- 间隙锁锁定范围(假设id=5不存在) SELECT * FROM products WHERE id BETWEEN 2 AND 6 FOR UPDATE; -- 锁定(2,6)区间[1,11](@ref)
四、总结对比
特性 | 行锁 | 表锁 |
---|---|---|
粒度 | 单行数据 | 整张表 |
并发性 | 高(仅阻塞冲突行) | 低(阻塞全表写操作) |
死锁风险 | 可能发生 | 无 |
适用引擎 | InnoDB | MyISAM/InnoDB(无索引时退化) |
典型场景 | 高并发OLTP(如电商交易) | 批量操作/结构变更 |
建议:
- 优先使用InnoDB引擎并合理设计索引,避免无索引导致的表锁
- 对高频写操作,通过缩小事务范围降低锁冲突概率
详细可参考