答案是通过分析SHOW ENGINE INNODB STATUS和information_schema中INNODB_LOCK_WAITS等表,可定位锁等待的等待方与持有方;常见原因为长事务、索引不合理、大批量操作、隔离级别不当及应用访问顺序不一致,优化策略包括拆分事务、添加索引、批量分批处理、调整隔离级别及统一资源访问顺序;死锁由InnoDB自动检测并回滚牺牲事务,需结合日志分析冲突SQL,通过短事务、一致访问顺序和重试机制应对。

MySQL锁等待问题,说白了,就是数据库里有事务因为要获取某个资源(比如一行数据、一个表)而被另一个事务“卡住”了,迟迟无法继续执行。排查这类问题,核心思路是找到是谁在等待、等待什么、以及谁持有了这个资源并导致了等待。
要排查MySQL的锁等待,我通常会从几个关键点入手。首先,最直接的证据往往藏在
SHOW ENGINE INNODB STATUS
LATEST DETECTED DEADLOCK
TRANSACTIONS
比如,当你在终端里敲下:
SHOW ENGINE INNODB STATUS;
你会看到一大段输出。你需要仔细阅读
TRANSACTIONS
trx_id
state
query
waiting for lock
has held lock(s)
如果发现有事务处于
waiting for lock
trx_id
再进一步,
information_schema
INNODB_LOCKS
INNODB_LOCK_WAITS
INNODB_TRX
INNODB_LOCKS
INNODB_LOCK_WAITS
INNODB_TRX
通过组合查询这些表,我们就能构建出完整的锁等待链条。
说实话,每次遇到锁等待,我最想做的就是马上找出那个“肇事者”和“受害者”。这有点像侦探破案,需要一点精准的查询。
我个人觉得,直接查询
information_schema.INNODB_LOCK_WAITS
一个经典的查询语句是这样的:
SELECT
lw.requesting_trx_id AS waiting_trx_id,
trx.trx_query AS waiting_query,
lw.blocking_trx_id AS blocking_trx_id,
b_trx.trx_query AS blocking_query,
b_lock.lock_mode,
b_lock.lock_type,
b_lock.lock_table,
b_lock.lock_index,
lw.wait_started,
trx.trx_state AS waiting_trx_state,
b_trx.trx_state AS blocking_trx_state,
trx.trx_mysql_thread_id AS waiting_thread_id,
b_trx.trx_mysql_thread_id AS blocking_thread_id
FROM
information_schema.INNODB_LOCK_WAITS lw
JOIN
information_schema.INNODB_TRX trx ON lw.requesting_trx_id = trx.trx_id
JOIN
information_schema.INNODB_TRX b_trx ON lw.blocking_trx_id = b_trx.trx_id
JOIN
information_schema.INNODB_LOCKS b_lock ON lw.blocking_trx_id = b_lock.trx_id
WHERE
b_lock.lock_mode IS NOT NULL
ORDER BY
lw.wait_started ASC;这条SQL语句的目的是将等待事务、持有锁事务以及它们各自正在执行的查询、锁的模式、锁定的表和索引等信息,全部整合到一起。
waiting_trx_id
waiting_query
blocking_trx_id
blocking_query
lock_table
lock_index
通过这个查询,你就能一目了然地看到谁在等谁,以及为什么等。这对于快速判断问题根源至关重要。
定位到问题之后,下一步就是分析原因并着手优化。我的经验告诉我,锁等待问题并非无迹可寻,它通常是以下几个原因造成的:
长事务:这是最常见的元凶。一个事务执行时间过长,比如包含了大量的数据操作,或者中间需要人工确认,那么它持有的锁就会长时间不释放,导致其他事务排队等待。
不合理的索引:如果查询没有命中索引,或者索引选择不当,InnoDB可能会进行全表扫描。即使是
UPDATE
DELETE
EXPLAIN
WHERE
JOIN
ORDER BY
GROUP BY
WHERE
大批量更新/删除:一次性更新或删除成千上万条数据,会长时间持有大量行锁。
COMMIT
事务隔离级别设置不当:MySQL InnoDB默认的隔离级别是
REPEATABLE READ
READ COMMITTED
READ COMMITTED
应用程序逻辑问题:有时候,问题不在数据库本身,而在应用程序对数据库的访问顺序。比如,两个事务都想更新A和B两张表,一个先锁A再锁B,另一个先锁B再锁A,这很容易造成死锁。
死锁是锁等待的一种特殊形式,它意味着两个或多个事务互相持有对方想要的锁,形成了一个闭环,谁也无法继续。MySQL InnoDB引擎会自动检测死锁并选择一个“牺牲品”(通常是修改行数最少的事务)进行回滚,以打破循环。虽然MySQL处理了,但对应用程序来说,这仍然是个错误,需要我们去分析并解决。
当死锁发生时,
SHOW ENGINE INNODB STATUS
LATEST DETECTED DEADLOCK
TRANSACTION (1)
TRANSACTION (2)
trx_id
query
WE ROLL BACK TRANSACTION (X)
分析死锁,核心在于理解这两个事务在“抢”什么资源。
举个例子,你可能会看到类似这样的输出:
*** (1) TRANSACTION: TRANSACTION 12345, ACTIVE 0 sec, process no 6789, OS thread id 1234567890 ... LOCK WAIT 2 lock(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 10, query id 100 localhost root updating UPDATE my_table SET value = 'new_val_A' WHERE id = 1; *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 123 page no 456 n bits 72 index `PRIMARY` of table `my_db`.`my_table` trx id 12345 lock_mode X locks rec but not gap waiting ... *** (2) TRANSACTION: TRANSACTION 54321, ACTIVE 0 sec, process no 9876, OS thread id 9876543210 ... LOCK WAIT 2 lock(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 20, query id 200 localhost root updating UPDATE my_table SET value = 'new_val_B' WHERE id = 2; *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 123 page no 456 n bits 72 index `PRIMARY` of table `my_db`.`my_table` trx id 54321 lock_mode X locks rec but not gap ...
这个例子可能不是一个典型的死锁,更像一个锁等待,但死锁的输出会更复杂,显示两个事务互相等待。你需要关注
WAITING FOR THIS LOCK TO BE GRANTED
HOLDS THE LOCK(S)
处理死锁的策略:
UPDATE
DELETE
WHERE
排查锁等待,尤其是死锁,确实需要一点耐心和对SQL执行细节的理解。但只要掌握了这些工具和方法,大部分问题都能迎刃而解。
以上就是mysql如何排查锁等待问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号