MySQL IS_USED_LOCK() 函数使用指南

MySQL IS_USED_LOCK() 是一个非阻塞的锁状态检测函数。它用于探查指定名称的咨询锁当前是否已被某个数据库连接持有,并在已被持有的情况下返回占用者的连接线程标识符(Connection ID)。

在分布式系统死锁排查、集群健康巡检以及运维审计场景中,直接通过 IS_USED_LOCK() 能够快速定位到底是哪一个活跃客户端线程卡住了关键业务锁,无需盲目重启服务。

MySQL IS_USED_LOCK() 语法

这里是 MySQL IS_USED_LOCK() 函数的标准语法:

IS_USED_LOCK(str)

参数

str
必需的。拟探查的目标命名锁名称字符串。

返回值

MySQL IS_USED_LOCK() 返回一个整数或 NULL:

  • 若目标锁当前正被某一连接占用:返回持有该锁的会话连接标识符(即 CONNECTION_ID() 对应的整数值)。
  • 若目标锁当前未被任何人使用(完全空闲):返回 NULL。

MySQL IS_USED_LOCK() 示例

以下示例演示如何探测锁所有权并结合系统表定位阻塞源头。

检查锁的占用状态

在 Session 1 中建立并持有锁:

-- Session 1 (查看自身 connection_id 为 14)
SELECT CONNECTION_ID();
SELECT GET_LOCK('export_excel_mutex', 5);

切换到 Session 2 或运维巡检终端探查该锁:

SELECT IS_USED_LOCK('export_excel_mutex') AS holding_connection_id;

输出:

+-----------------------+
| holding_connection_id |
+-----------------------+
|                    14 |
+-----------------------+

如果锁未被占用:

SELECT IS_USED_LOCK('an_idle_task_lock') AS holding_connection_id;

输出:

+-----------------------+
| holding_connection_id |
+-----------------------+
|                  NULL |
+-----------------------+

结合 PROCESSLIST 快速定位持锁客户端来源

当发现某一锁长时间不释放时,可以通过返回的连接 ID 联合 information_schema.processlist 追查该客户端的 IP、执行时长与当前状态:

SELECT 
    p.id AS conn_id,
    p.user,
    p.host,
    p.db,
    p.command,
    p.time AS running_seconds,
    p.state
FROM information_schema.processlist p
WHERE p.id = IS_USED_LOCK('export_excel_mutex');

输出:

+---------+------+--------------------+---------+---------+-----------------+-------+
| conn_id | user | host               | db      | command | running_seconds | state |
+---------+------+--------------------+---------+---------+-----------------+-------+
|      14 | app  | 192.168.1.50:52410 | shop_db | Sleep   |             120 |       |
+---------+------+--------------------+---------+---------+-----------------+-------+

查询结果直接暴露了位于 192.168.1.50 的客户端处于 Sleep 状态但未归还锁,运维人员可依此采取通知或 KILL 14; 措施。

结论

  • IS_USED_LOCK() 本身纯粹读取锁状态,不会阻塞当前会话,也不会对目标锁加锁。
  • 可以使用 IS_USED_LOCK(str) = CONNECTION_ID() 表达式判断当前会话是否为该锁的主人,从而实现防御式代码重入保护。
  • 绝不要用 IS_USED_LOCK() IS NULL 来决定随后是否调用 GET_LOCK(),因为探查与加锁之间存在非原子性的时间窗口,应当直接调用 GET_LOCK(str, timeout) 并依据其返回值进行分支控制。