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

1次阅读

调大innodb_lock_wait_timeout不能解决锁冲突本质问题,仅延长等待时间;根因在于长事务、未走索引的DML、事务内耗时操作等,需从sql设计、索引优化、事务边界入手。

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

为什么 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 分钟的事务
  • 检查这些事务里有没有 UPDATEdelete 没命中索引(看 EXPLAINtype 是否为 ALLindex
  • 避免在事务内调用外部服务、生成 PDF、发邮件等耗时操作

如何快速定位谁在锁谁

mysql 8.0+ 可直接查 performance_schema.data_locksdata_lock_waits,但更通用且直观的方式是组合查 INNODB_TRXINNODB_LOCKS(5.7)、INNODB_LOCK_WAITS(已废弃,但兼容性好),或直接用 sys.innodb_lock_waits 视图。

实操建议:

  • 执行 select * FROM sys.innodb_lock_waitsG,它会直接显示阻塞者和被阻塞者的 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/DELETEWHERE 字段,确认是否都有单列索引或复合索引前导列匹配
  • EXPLAIN format=tree(8.0)或 EXPLAINkey 列是否为 NULLrows 是否远大于实际匹配数
  • 特别警惕 OR、函数包装字段(如 WHERE DATE(create_time) = '2024-01-01')、隐式类型转换varchar 字段传数字)

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

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

实操建议:

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

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

text=ZqhQzanResources