mysql如何优化锁的等待时间_mysql锁等待优化

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

为什么
innodb_lock_wait_timeout
调大不一定解决问题

这个参数只控制事务在等待行锁时最多忍多久,超时就报

Lock wait timeout exceeded
。但它不减少锁冲突本身,只是让失败来得更快或更慢。真实瓶颈往往在事务设计:长事务、没走索引的
UPDATE
、在事务里做 RPC 或文件操作,都会把锁占着不动。

实操建议:

SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60
找出运行超 1 分钟的事务
检查这些事务里有没有
UPDATE
DELETE
没命中索引(看
EXPLAIN
type
是否为
ALL
index
避免在事务内调用外部服务、生成 PDF、发邮件等耗时操作

如何快速定位谁在锁谁

MySQL 8.0+ 可直接查

performance_schema.data_locks
data_lock_waits
,但更通用且直观的方式是组合查
INNODB_TRX
INNODB_LOCKS
(5.7)、
INNODB_LOCK_WAITS
(已废弃,但兼容性好),或直接用
sys.innodb_lock_waits
视图。

实操建议:

执行
SELECT * FROM sys.innodb_lock_waits\G
,它会直接显示阻塞者和被阻塞者的
trx_id
、SQL、等待时间
对应到
SELECT * FROM INNODB_TRX WHERE trx_id = 'xxx'
查具体事务语句和启动时间
注意
trx_state = 'LOCK WAIT'
表示正在等锁,
trx_state = 'RUNNING'
trx_started
很早,很可能是“钉子户”

UPDATE/DELETE 没走索引导致全表扫描加锁

InnoDB 行锁是建立在索引上的;如果

WHERE
条件没命中索引,就会退化成表级意向锁 + 大量记录的间隙锁或记录锁,极大增加冲突概率。常见于
UPDATE user SET status=1 WHERE phone='138...' AND deleted=0
,但
phone
没建索引。

实操建议:

对所有
UPDATE
/
DELETE
WHERE
字段,确认是否都有单列索引或复合索引前导列匹配
EXPLAIN FORMAT=tree
(8.0)或
EXPLAIN
key
列是否为
NULL
rows
是否远大于实际匹配数
特别警惕
OR
、函数包装字段(如
WHERE DATE(create_time) = '2024-01-01'
)、隐式类型转换(
varchar
字段传数字)

事务隔离级别对锁等待的影响

默认

REPEATABLE READ
下,普通
SELECT
不加锁,但
UPDATE
/
DELETE
会加记录锁 + 间隙锁(防止幻读),锁范围更大、持有时间更长;而
READ COMMITTED
只加记录锁,间隙锁只在唯一索引等少数场景生效,锁更轻。

实操建议:

业务能接受“不可重复读”的,可考虑将库或会话设为
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
注意
READ COMMITTED
UPDATE ... WHERE
仍可能锁住非匹配行(如唯一索引查找失败时的插入意向锁),不能完全消除等待
避免在高并发写场景下使用
SERIALIZABLE
,它会让普通
SELECT
也加共享锁,极易雪崩

锁等待优化最常被忽略的一点:不是调参或加索引就能一劳永逸,而是要从应用层明确每条 SQL 的锁生命周期——它什么时候开始持锁?持的是什么粒度的锁?其他事务哪条语句可能和它重叠?这些问题比“怎么查锁”更关键。

相关推荐