MySQL GET_LOCK() 函数使用指南

MySQL GET_LOCK() 是一个内置的用户级应用咨询锁(Advisory Lock)函数。在多实例或多进程的分布式业务架构中,当多个客户端连接需要协调执行某一独占任务(例如执行定时数据汇总、刷新全站缓存或防止重复扣款)时,直接依赖行锁或表锁往往会造成过重的数据库元数据争用与事务挂起。

GET_LOCK() 允许应用程序在 MySQL 内部创建一个由自定义字符串命名的轻量级独占锁。该锁独立于具体的表和行数据,仅在数据库会话之间生效,能够在不触碰业务数据表的前提下提供高效率的互斥同步保障。

MySQL GET_LOCK() 语法

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

GET_LOCK(str, timeout)

参数

str
必需的。锁的自定义名称字符串。在 MySQL 5.7 及更高版本中,锁名最大允许 64 个字符,且名称比对遵循当前连接字符集的排序规则(通常大小写不敏感)。
timeout
必需的。等待获取锁的超时时间,单位为秒(支持整数或浮点数)。
  • 若设置为大于 0 的数值,客户端在无法立即获得锁时最多等待该秒数;
  • 若设置为 0,表示非阻塞模式,无法立即获取时直接放弃;
  • 若设置为负数(如 -1),表示无限期等待,直到成功获取锁或连接被终止。

返回值

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

  • 1:成功获取锁。
  • 0:在指定的 timeout 秒数内未能获取锁(锁正被其他连接占用)。
  • NULL:发生异常(例如传入非法参数、系统内存耗尽或该线程被管理员使用 KILL 终止)。

[!NOTE] 从 MySQL 5.7.5 开始,单个客户端连接可以同时持有多个不同的命名锁。而在更早的 MySQL 5.6 版本中,同一个会话再次调用 GET_LOCK() 会隐式释放之前获取的旧锁。

MySQL GET_LOCK() 示例

以下示例演示 GET_LOCK() 的常用业务场景与并发控制方式。

基础用法:获取独占锁与超时测试

在第一个数据库会话(Session 1)中获取名为 'daily_report_lock' 的任务锁:

SELECT GET_LOCK('daily_report_lock', 10) AS lock_result;

输出:

+-------------+
| lock_result |
+-------------+
|           1 |
+-------------+

此时打开第二个数据库会话(Session 2),尝试在 3 秒超时内获取同一个锁:

SELECT GET_LOCK('daily_report_lock', 3) AS lock_result;

输出:

+-------------+
| lock_result |
+-------------+
|           0 |
+-------------+

返回 0 说明锁已被其他客户端占用,Session 2 在 3 秒后超时退出,避免了任务被两个进程重复执行。

分布式定时任务防重调度实践

在微服务后端定时任务中,常利用 GET_LOCK() 进行抢占式调度:

-- 尝试立即抢占任务锁(0 秒超时)
SELECT GET_LOCK('cron_job_sync_user', 0) AS is_leader;

-- 业务执行完毕后显式释放
SELECT RELEASE_LOCK('cron_job_sync_user') AS release_status;

结论

  • GET_LOCK() 是 MySQL 提供的极具性价比的应用层互斥原语,避免了在 Redis 或 Zookeeper 等外部组件故障时单点任务失去锁保护的风险。
  • 锁与事务无关:执行 COMMIT 或 ROLLBACK 不会释放由 GET_LOCK() 持有的锁。锁仅在客户端显式调用 RELEASE_LOCK()、RELEASE_ALL_LOCKS() 或连接彻底断开时释放。
  • 建议在应用程序的 finally 块中始终配对调用 RELEASE_LOCK(),避免因长连接复用导致锁未能及时归还。