MySQL 锁定服务的命名空间与锁名怎样区分
来源:17golang原创
时间:2026-11-10 09:43:24 146浏览 收藏
我在把多个业务模块接入 MySQL 锁定服务时,最先固定的不是超时时间,而是锁的命名规则。原因很简单:锁不是只由一个字符串决定,而是由“命名空间 + 锁名”共同识别。只要命名空间不同,两个应用就可以使用相同的锁名;命名空间相同而锁名不同,则是同一业务域中的两个独立资源。
官方地址:https://dev.mysql.com/doc/refman/8.4/en/locking-service.html
可以把 MySQL 锁定服务的锁标识理解为 (namespace, lock_name) 二元组。命名空间负责隔离应用或业务域,锁名负责指出域内的具体资源;读写模式和超时则描述这次占用怎样协作。
命名空间和锁名共同组成锁标识
MySQL 文档把锁描述为三个属性:锁命名空间、锁名和锁模式。真正用于判断冲突的是前两个属性的组合,模式决定同一标识上的共享或排他关系。

例如,下面四个字符串不能单独看成四把“全局锁”,而应按二元组理解:
-- 两个业务域可以安全复用 invoice:42 这个锁名
SELECT service_get_write_locks('billing', 'invoice:42', 5);
SELECT service_get_write_locks('shipping', 'invoice:42', 5);
-- 同一个业务域中,invoice:42 与 invoice:43 是两个不同锁标识
SELECT service_get_read_locks('billing', 'invoice:42', 5);
SELECT service_get_read_locks('billing', 'invoice:43', 5);
上面的 SQL 假定锁定服务函数已经安装。第一组调用的标识分别是 (billing, invoice:42) 和 (shipping, invoice:42),命名空间不同,所以不会因为锁名相同而互相冲突。
先按业务域划分 namespace,再设计 lock_name
我更倾向于让 namespace 表示“谁负责这类协作”,让 lock_name 表示“域内哪个资源”。比如账单模块可以使用 billing,锁名再使用 invoice:42;仓储模块可以使用 inventory,锁名使用 sku:42。
这种划分比把所有信息拼成一个长字符串更容易排查,因为在 Performance Schema 中,命名空间会落到 OBJECT_SCHEMA,锁名会落到 OBJECT_NAME。命名时建议遵循三个习惯:
- namespace 固定为稳定的业务或应用边界,不把一次请求 ID 放进去。
- lock_name 表达域内资源,例如
invoice:42、user:1008,不要只用无法解释的数字。 - 在同一团队中统一大小写和分隔符,因为锁定服务把名称当作二进制字符串比较,大小写敏感。
命名空间和锁名都不能是 NULL、空字符串,长度也不能超过 64 个字符。名称规则先定下来,后面看到等待锁时,才能迅速判断是跨模块竞争还是同模块资源竞争。
读锁、写锁和 timeout 要一起决定
锁模式不是命名的一部分,但它决定同一个锁标识上的并发关系。读锁是共享的,多个会话可以同时持有;写锁是排他的,其他会话不能再获取该标识的读锁或写锁。
-- 读场景允许共享访问;timeout=0 表示不等待
SELECT service_get_read_locks('billing', 'invoice:42', 0);
-- 更新场景要求排他访问;最多等待 5 秒
SELECT service_get_write_locks('billing', 'invoice:42', 5);
-- 失败时应让调用方记录错误并决定重试或返回
-- 不要把超时当成事务自动回滚的信号
timeout 是等待秒数,设置为 0 就是不等待。如果在等待期间发生超时,服务会返回超时错误;不同会话之间形成死锁时,也可能返回死锁错误。这里要特别注意:锁定服务的死锁错误不会替调用方回滚事务,应用仍然要自行决定如何处理。
当一次调用要取得多个锁时,这一次调用内部是原子的:只有全部取得才算成功,某一个锁失败时本次调用不取得任何锁。但这种原子性不会自动跨越多次调用,所以批量资源最好在能接受的范围内合并请求,并给锁名排序规则。
用 metadata_locks 看清到底锁了什么
只看应用日志里的“获取成功”很容易误判。锁定服务使用元数据锁框架,可以在 Performance Schema 的 metadata_locks 表里对照锁的命名空间、锁名、模式和状态。排查时我通常先建立一个最小观察查询:
-- 只查看锁定服务产生的元数据锁,减少无关对象干扰
SELECT OBJECT_SCHEMA,
OBJECT_NAME,
LOCK_TYPE,
LOCK_STATUS
FROM performance_schema.metadata_locks
WHERE OBJECT_TYPE = 'LOCKING SERVICE';

字段可以按下面的方式阅读:
| 字段 | 对应含义 | 排查重点 |
|---|---|---|
OBJECT_SCHEMA | 锁命名空间 | 确认是否进入了预期业务域 |
OBJECT_NAME | 锁名 | 确认资源编号和大小写是否一致 |
LOCK_TYPE | SHARED 或 EXCLUSIVE | 判断是读协作还是写冲突 |
LOCK_STATUS | GRANTED 或 PENDING | 判断已经持有还是正在等待 |
如果一个会话持有 (billing, invoice:42) 的排他锁,另一个会话对同一二元组发起读锁请求,那么第二个会话可能显示为 PENDING。如果两个请求使用的 namespace 不同,即使 OBJECT_NAME 相同,也不应仅凭锁名判断它们存在冲突。
释放按 namespace 发生,和事务提交不是一回事
锁定服务的锁属于会话级资源。调用 service_release_locks(namespace) 可以释放当前会话在该命名空间中通过锁定服务取得的锁;会话终止时锁也会被释放。它们不会因为事务提交或回滚就自动消失。
-- 业务处理完成后按命名空间释放当前会话持有的锁
SELECT service_release_locks('billing');
-- 该释放动作是显式的;COMMIT/ROLLBACK 不代替它
COMMIT;
这也是命名空间设计需要提前考虑的地方:如果一个会话同时服务多个业务域,释放时应明确调用哪个 namespace,避免把“事务结束”错误地当成“锁已经释放”。应用层最好在成功、失败和连接异常路径都安排释放策略,并把 namespace、lock_name、mode 和 timeout 记录到结构化日志。
我会怎样把它落到项目规范里
在实际接入前,可以先用一张小表完成基线记录:请求业务域、锁标识、锁模式、等待秒数、成功/超时次数和最大持有时长。这样优化前后比较的是同一组指标,而不是只凭“感觉变快了”。
- 先确认 namespace 是否代表稳定边界,避免把用户输入直接当命名空间。
- 再确认 lock_name 是否能定位到域内资源,必要时统一前缀和大小写。
- 用读锁表达可共享访问,用写锁保护变更,并给 timeout 设定业务可接受的等待上限。
- 发现等待时,同时查看
OBJECT_SCHEMA、OBJECT_NAME和LOCK_STATUS,不要只查锁名。 - 将释放动作绑定在会话生命周期上,而不是只绑定在事务提交或回滚上。
如果项目只需要一个简单的会话互斥锁,还要注意 MySQL 的 GET_LOCK() 是另一套接口:它不提供命名空间,也不提供独立的读锁和写锁。需要业务域隔离或共享/排他模式时,应先确认选用的是锁定服务接口。
常见问题
namespace 和 lock_name 可以写成同一个字符串吗?
可以,但这样会失去业务域与资源层次的可读性。只要二者组合后能稳定标识资源,技术上并不要求它们必须不同。
同一个会话重复获取同一锁会怎样?
同一会话可以对相同的二元组取得多个锁实例,因此释放时要按命名空间释放,而不是假设一次释放只对应一次获取。
事务回滚后为什么锁还在?
因为锁定服务的锁不是事务锁。它会由显式释放函数释放,或在会话终止时隐式释放。
总结
记住一个判断式就够了:(namespace, lock_name) 定义“是不是同一把锁”,mode 定义“能否共享”,timeout 定义“最多等多久”。把命名空间当作业务隔离,把锁名当作域内资源,再用 metadata_locks 的四个字段观察真实状态,排查锁冲突会比只盯着一个字符串可靠得多。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
416 收藏
-
466 收藏
-
201 收藏
-
116 收藏
-
453 收藏
-
248 收藏
-
329 收藏
-
237 收藏
-
430 收藏
-
487 收藏
-
436 收藏
-
数据库 · MySQL | 10小时前 | MySQL · InnoDB · 数据库排查 · mysql innodb 隔离级别 间隙锁 READ COMMITTED REPEATABLE READ next-key lock141 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习