mysql中的锁粒度与性能优化的关系

来源:这里教程网 时间:2026-02-28 20:51:22 作者:

表级锁 vs 行级锁:什么时候会退化成表锁

MySQL 的锁粒度直接影响并发能力,但实际中

InnoDB
并非总能用上行级锁。比如执行
UPDATE t SET a=1 WHERE b LIKE '%abc%'
时,若
b
列没有索引,优化器无法定位具体行,就会对全表加
X
锁(即表级写锁)。这种退化在慢查询日志里常表现为
Rows_examined
远大于
Rows_affected

有主键或唯一索引的等值条件(
WHERE id = 123
)→ 精确行锁
范围查询(
WHERE created_at > '2024-01-01'
)→ 可能锁住间隙(Gap Lock),甚至整个索引段
无索引字段 + 条件 → 全表扫描 + 表级锁(即使引擎是 InnoDB)
SELECT ... FOR UPDATE
在 RC 隔离级别下不加 Gap Lock,但在 RR 下会,影响并发插入

间隙锁(Gap Lock)为何让“插入变慢”

间隙锁是 InnoDB 实现可重复读(RR)的关键机制,但它会锁住索引中不存在的“空档”。例如

id
是主键,当前有记录
(1),(5),(10)
,执行
SELECT * FROM t WHERE id BETWEEN 3 AND 7 FOR UPDATE
,InnoDB 会锁住 (1,5) 和 (5,10) 这两个间隙 —— 此时
INSERT INTO t VALUES (4)
就会被阻塞。

间隙锁只存在于 RR 隔离级别;RC 下不启用,但幻读风险上升 唯一索引的等值查询(含主键)不会触发间隙锁,只锁匹配行 非唯一索引的等值查询仍可能锁间隙(如
name='alice'
,而 name 不唯一)
SELECT ... LOCK IN SHARE MODE
同样受间隙锁影响,不只是
FOR UPDATE

如何用
SHOW ENGINE INNODB STATUS
查真实锁等待

仅看

PROCESSLIST
或慢日志不够,真正卡在哪、谁在等谁,得靠 InnoDB 自带的状态快照。执行后重点关注
TRANSACTIONS
LOCK WAIT
段落。

SHOW ENGINE INNODB STATUS\G
---TRANSACTION [hex_id], ACTIVE [sec] sec
行,确认事务是否长时间未提交
看到
lock_mode X locks rec but not gap waiting
→ 正在等某一行锁释放
看到
lock_mode X locks gap before rec insert intention waiting
→ 被间隙锁堵住插入
Trx has been waiting [n] sec for this lock
超过 5 秒就该警惕了
注意
mysql tables in use [n], locked [m]
中 locked > 1 可能是隐式锁升级

降低锁冲突的实操建议

锁不是越细越好,而是要和业务节奏对齐。盲目拆分事务或加索引反而引入新问题。

写操作尽量走主键或覆盖索引,避免回表 → 减少锁行数 批量更新改用
INSERT ... ON DUPLICATE KEY UPDATE
替代先查后更,减少事务持有时间
高并发计数场景(如点赞数)别直接
UPDATE t SET cnt = cnt + 1
,改用 Redis 计数+异步落库
长事务必须拆:比如导出报表,不要在一个事务里查 10 万行再统一更新,分页处理并及时提交 避免在事务中调用外部 HTTP 接口或 sleep(),锁会一直持有着

锁粒度和性能之间没有银弹,关键在理解每一行 SQL 落到索引树上的真实路径 —— 看执行计划只是起点,看锁状态才是真相。

相关推荐