mysql中的锁定等待与事务优先级配置

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

如何识别正在发生的锁等待

MySQL 中出现性能抖动或查询卡住,大概率是事务在等锁。最直接的判断方式是查

information_schema.INNODB_TRX
information_schema.INNODB_LOCK_WAITS

SELECT 
  r.trx_id waiting_trx_id,
  r.trx_mysql_thread_id waiting_thread,
  r.trx_query waiting_query,
  b.trx_id blocking_trx_id,
  b.trx_mysql_thread_id blocking_thread,
  b.trx_query blocking_query
FROM information_schema.INNODB_LOCK_WAITS w
JOIN information_schema.INNODB_TRX b ON b.trx_id = w.BLOCKING_TRX_ID
JOIN information_schema.INNODB_TRX r ON r.trx_id = w.REQUESTING_TRX_ID;

注意:

INNODB_LOCK_WAITS
在 MySQL 8.0.1 之后才稳定可用;5.7 及更早版本需依赖
SHOW ENGINE INNODB STATUS
解析 LOCK WAIT 行,但输出不易解析且无实时性。

常见误判点:

看到
trx_state = RUNNING
不代表没锁——它只表示事务没提交,实际可能正持锁阻塞他人
trx_started
时间远早于当前时间,说明该事务长期未提交,极可能是“长事务”,应优先 kill
waiting_query
NULL
,说明等待发生在非 SQL 层(如主键查找、唯一约束校验),需结合
trx_operation_state
判断

innodb_lock_wait_timeout 能否控制事务优先级

不能。

innodb_lock_wait_timeout
只是设置单个语句等待锁的**超时秒数**,超时后报错
ERROR 1205 (40001): Deadlock found when trying to get lock...
ERROR 1205 (40001): Lock wait timeout exceeded...
,它不参与锁争抢时的调度决策。

MySQL InnoDB **没有事务优先级抢占机制**:不会因为某个事务启动早、线程 ID 小、或用了

SET SESSION TRANSACTION ISOLATION LEVEL
就优先获得锁。锁分配完全基于“先到先得 + 死锁检测后回滚”原则。

真正影响“谁被杀”的只有死锁检测器的判定逻辑:

InnoDB 总是回滚 undo log 量更小 的那个事务(即修改行数更少、产生的回滚段更少) 这个选择不可配置,也与事务执行时间、客户端 IP、SQL 类型无关 想降低被回滚概率?尽量让写操作批量、紧凑,避免在事务中穿插大量 SELECT 或网络 I/O

用 low_priority_updates 和 innodb_thread_concurrency 控制并发行为

这两个参数常被误认为能“提升事务优先级”,实际作用非常有限,且多数场景已不推荐使用:

low_priority_updates=ON
:仅对
INSERT/UPDATE/DELETE
生效,使其让位于
SELECT
—— 但这会加剧写入延迟,且在读多写少系统中反而放大锁等待
innodb_thread_concurrency
:设为非 0 值(如 16)会启用 InnoDB 内部线程调度器,但该机制在 MySQL 5.6.2+ 后默认禁用(值为 0),因实测效果不佳且增加调度开销
真正可控的并发调节应落在应用层:例如用连接池限制最大活跃写事务数,或对高冲突业务加应用级分布式锁

替代方案更有效:

SELECT ... FOR UPDATE SKIP LOCKED
避免无谓等待(适用于队列类消费场景)
将大事务拆成小事务,减少单次持锁时间 确保 WHERE 条件命中索引,避免锁升级为表级锁(如全表扫描触发
gap lock

为什么 SET TRANSACTION ISOLATION LEVEL 不影响锁等待顺序

隔离级别决定的是“能看到什么数据”,不是“谁能先拿到锁”。

READ COMMITTED
REPEATABLE READ
对锁的行为差异集中在:

READ COMMITTED
:只对当前读(
SELECT ... FOR UPDATE
UPDATE
)加行锁,不加 gap lock(除非唯一索引等特殊情况)
REPEATABLE READ
:默认加 next-key lock(行锁 + gap 锁),范围更广,更容易造成锁冲突

但无论哪种级别,两个事务同时更新同一行时,InnoDB 仍按内部等待队列顺序处理,不会因为 A 是 RC 级别、B 是 RR 级别就让 A 先获得锁。

容易忽略的关键点:

显式开启事务(
BEGIN
)后第一个 SELECT 才触发一致性读快照,此前的 UPDATE 已经持锁——隔离级别生效时机比直觉中晚
autocommit=0
下忘记
COMMIT
,会导致锁长期持有,这是线上锁等待头号原因
START TRANSACTION WITH CONSISTENT SNAPSHOT
并不能避免锁等待,它只影响读视图,不影响写锁获取

相关推荐