lock_time过长说明查询主要卡在锁等待而非执行本身,与rows_examined无关;若lock_time远大于query_time(如0.001s vs 2.3s),表明几乎全在等锁,需立即查innodb_trx和innodb_lock_waits定位持锁事务。

Lock_time 过长,基本说明查询卡在了行锁或表锁上,不是 SQL 本身慢,而是等锁时间太长。
怎么看 Lock_time 和 Rows_examined 的关系?
MySQL 慢查询日志里,Lock_time 和 Rows_examined 是两个独立指标,不能直接相加得出总耗时。常见误判是:看到 Rows_examined 少、Lock_time 高,就以为“扫描快但锁久”,其实更可能是事务持有锁太久,导致后续查询排队。
-
Lock_time是该语句实际等待锁的时间(单位秒,精度微秒),不包括执行时间 -
Rows_examined是该语句执行过程中遍历的索引/数据行数,和锁等待无关 - 如果
Lock_time明显大于Query_time(比如Query_time: 0.001s,Lock_time: 2.3s),说明几乎全在等锁
如何定位谁在持锁不放?
别只盯慢日志,得立刻查当前锁状态。MySQL 8.0+ 推荐用 performance_schema,5.7 可用 information_schema.INNODB_TRX + INNODB_LOCK_WAITS。
- 先看有没有长时间运行的事务:
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60; - 再查这些事务是否在等锁:
SELECT * FROM information_schema.INNODB_LOCK_WAITS; - 结合
INNODB_LOCKS(5.7)或performance_schema.data_locks(8.0+)看具体锁在哪张表、哪个索引、哪一行 - 特别注意
trx_isolation_level = 'READ-COMMITTED'下仍可能因间隙锁(gap lock)阻塞,不只看主键更新
哪些写法容易引发隐式锁等待?
很多 Lock_time 高的问题,源头不在慢查询本身,而在上游事务的写法松散。
- 事务中混用 DML 和慢查询(比如先
UPDATE再SELECT SLEEP(5)),导致锁持有时间远超必要 - 没走索引的
WHERE条件(如LIKE '%abc')会升级为表级锁扫描,连带锁住大量无关行 - 批量
DELETE或UPDATE缺少LIMIT,一次锁几千行,下游所有涉及该表的查询都排队 - 应用层手动开启事务后,忘记
COMMIT或ROLLBACK(尤其异常分支),锁会一直挂着
怎么验证是不是锁竞争导致的?
最直接的办法是复现时抓实时锁视图,而不是只翻慢日志。另外可以临时调整隔离级别观察变化:
- 对非一致性读场景,尝试把事务设为
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;—— 如果 Lock_time 显著下降,基本锁定是 MVCC 版本链或间隙锁争用 - 用
pt-deadlock-logger持续采集死锁日志,虽然它不记录普通锁等待,但高频死锁往往预示着锁粒度或顺序问题已很严重 - 检查慢查询对应语句是否出现在多个事务中作为“最后执行的语句”——这说明它常被当作锁等待的受害者,而非加锁者
真正难排查的是跨服务、跨数据库连接池的长事务,比如一个 HTTP 请求开了事务,中间调用外部 API 耗时几秒,回来才提交。这种不会出现在单条慢日志里,得靠全链路 trace 对齐线程 ID 和 trx_id。











