MySQL用GET_LOCK()可实现咨询锁,但仅限单实例、会话级,不支持跨节点、无TTL、不参与事务回滚;推荐用于轻量协作,生产级分布式锁应选唯一索引表或Redis。

MySQL里没有 advisory_lock(),但可以用 GET_LOCK() 替代
MySQL 没有 PostgreSQL 那样的 pg_advisory_lock() 系列函数,但内置了 GET_LOCK() 和 RELEASE_LOCK(),能实现用户级咨询锁(advisory lock)的核心语义:不依赖表结构、由应用控制生命周期、跨会话协调。
关键区别在于:GET_LOCK() 是会话级的,锁名是字符串,且同一会话重复获取同名锁不会阻塞(直接返回 1),而不同会话之间才会真正互斥。它不绑定事务,RELEASE_LOCK() 显式释放,或会话断开时自动释放。
-
GET_LOCK('my_app:task_123', 10)表示尝试获取名为'my_app:task_123'的锁,最多等待 10 秒;返回 1 成功,0 超时,NULL 出错 - 锁名建议带命名空间前缀(如
'service_name:key'),避免不同模块冲突 - 不能在存储函数中调用(只允许在存储过程、触发器或客户端 SQL 中使用)
- 注意:MySQL 8.0.19+ 才支持在 prepared statement 中使用
GET_LOCK()
如何安全地配合事务使用咨询锁
咨询锁本身不参与事务回滚,所以如果在事务中获取了锁,但事务失败后没手动释放,锁会一直持有到会话结束——这是最常见的泄漏来源。
推荐做法是「锁即临界区入口」,把锁获取放在事务最外层,并确保无论成功或失败都释放:
SET @lock_name = 'order:12345'; SELECT GET_LOCK(@lock_name, 30) INTO @acquired; IF @acquired = 1 THEN START TRANSACTION; -- 执行业务逻辑(如更新订单状态) UPDATE orders SET status = 'processing' WHERE id = 12345; COMMIT; ELSE SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Failed to acquire advisory lock'; END IF; SELECT RELEASE_LOCK(@lock_name);
- 不要把
GET_LOCK()放在BEGIN ... END块内部再嵌套事务,容易逻辑混乱 - 若用在应用层(如 Python/Go),应在 defer / finally 中调用
SELECT RELEASE_LOCK(...),而不是依赖连接池自动关闭 - 避免在长事务中持锁过久——咨询锁阻塞的是其他会话的
GET_LOCK()调用,不是数据行,但长时间占用仍影响并发吞吐
GET_LOCK() 的常见失效场景和排查方式
看似调用了 GET_LOCK() 却没起到互斥作用?大概率掉进了这几个坑:
- 锁名包含空格、特殊字符或长度超 64 字节(MySQL 限制)→ 实际生效的是截断后的名字,导致不同意图锁名意外相同
- 客户端连接设置了
autocommit=1,又没显式捕获GET_LOCK()返回值 → 错误被忽略,后续逻辑照常执行 - 多个应用实例连的是不同 MySQL 节点(如读写分离架构下写节点不唯一)→
GET_LOCK()只在单实例内存中生效,跨节点完全无效 - 执行
SELECT IS_USED_LOCK('name')返回非 NULL,说明锁正被某会话持有,可查SELECT * FROM performance_schema.threads WHERE processlist_id = ?定位源头
替代方案:什么时候该放弃 GET_LOCK() 改用行锁或外部服务
当出现以下情况时,GET_LOCK() 就不再是合适选择:
- 需要跨 MySQL 实例协调(比如分库场景)→ 行锁无用,得上 Redis 的
SET key value NX EX 30或 etcd - 锁粒度必须精确到记录级别,且要与事务强一致 → 直接用
SELECT ... FOR UPDATE更可靠,虽然有死锁风险但语义清晰 - 应用已重度依赖 Redis → 别硬套 MySQL 咨询锁,Redis 的原子 set + lua 脚本释放更轻量、可观测性更好
- 需要锁续期(lease renew)或健康探测 →
GET_LOCK()不支持,必须自己维护心跳和超时清理逻辑,复杂度陡增
咨询锁本质是“大家约定遵守”的机制,MySQL 的 GET_LOCK() 提供了基础能力,但它的边界非常明确:单实例、短时、应用层主动管理。越试图把它撑成分布式锁,越容易在超时、网络分区、进程崩溃时出问题。


















