golang,go,博客,开源,编程
GET_LOCK()
详解GET_LOCK()
是 MySQL 提供的一个 用户级锁函数,用于在会话(Session)级别创建一个 命名互斥锁(named mutex lock),用于** 同步多个会话**,防止资源冲突。
GET_LOCK()
语法GET_LOCK(lock_name, timeout)
lock_name
:要创建的锁的名称,必须是字符串(最大 64 字节)。timeout
:等待锁的时间(秒)。
0
代表立即返回,不等待。-1
代表无限等待,直到获取到锁。0
。返回值 | 说明 |
---|---|
1 | 获取锁成功 |
0 | 获取锁失败(可能因为超时或锁已被其他会话占用) |
NULL | 发生错误(如连接断开) |
GET_LOCK()
示例SELECT GET_LOCK('my_lock', 10);
1
(成功)。10
秒,如果还没释放,则返回 0
。SELECT RELEASE_LOCK('my_lock');
返回值 | 说明 |
---|---|
1 | 释放成功 |
0 | 释放失败(该锁不存在或不是当前会话持有的锁) |
NULL | 发生错误 |
⚠️ 只有持有锁的会话才能释放锁!其他会话无法释放该锁。
SELECT GET_LOCK('my_lock', 0);
1
,表示成功获取。0
,不会等待。SELECT GET_LOCK('my_lock', -1);
1
,表示成功获取。GET_LOCK()
SELECT GET_LOCK('my_lock', 10);
✅ 返回 1
,获取锁成功。
SELECT GET_LOCK('my_lock', 5);
⏳ 会话 B 需要等待 最多 5 秒。
1
。0
(超时)。SELECT RELEASE_LOCK('my_lock');
✅ 返回 1
,表示锁已释放。
1
。GET_LOCK()
的特点可以创建多个独立的锁:
SELECT GET_LOCK('lock_1', 10);
SELECT GET_LOCK('lock_2', 10);
每个锁都是独立的,互不影响。
如果 同一会话 先获取了 lock_1
,然后又获取 lock_2
,则 lock_1
会自动释放:
SELECT GET_LOCK('lock_1', 10); -- 获取 lock_1,返回 1
SELECT GET_LOCK('lock_2', 10); -- 获取 lock_2,返回 1,同时 lock_1 被释放
💡 解决方案:使用 LOCK TABLES
或 advisory lock
。
如果会话 A 获取了 GET_LOCK('my_lock', 10)
,然后会话 A 断开连接,MySQL 会自动释放该锁,其他会话可以重新获取它。
GET_LOCK()
vs LOCK TABLES
vs advisory locks
方式 | 作用范围 | 是否会自动释放 | 适用场景 |
---|---|---|---|
GET_LOCK() | 会话级别 | 仅限当前会话 | 同步不同 SQL 任务 |
LOCK TABLES | 数据库表级 | 断开连接会释放 | 需要锁定整个表的事务 |
advisory lock (pg_advisory_lock in PostgreSQL) | 进程级 | 需要手动释放 | 多进程同步 |
GET_LOCK()
的应用场景在多个会话同时更新同一行数据时,使用 GET_LOCK()
可以确保只有一个会话能执行更新:
SELECT GET_LOCK('update_user_1', 10);
UPDATE users SET balance = balance - 100 WHERE id = 1;
SELECT RELEASE_LOCK('update_user_1');
如果另一个会话尝试 GET_LOCK('update_user_1', 10)
,它必须等当前会话完成后才能执行。
如果多个表需要一起更新,可以用 GET_LOCK()
确保事务一致性:
START TRANSACTION;
SELECT GET_LOCK('order_123', 10);
UPDATE orders SET status = 'paid' WHERE id = 123;
UPDATE payments SET confirmed = 1 WHERE order_id = 123;
SELECT RELEASE_LOCK('order_123');
COMMIT;
这样可以防止其他事务同时修改 order_123
。
如果有一个 定时任务,但不希望多个实例同时运行:
SELECT GET_LOCK('daily_report', 0);
1
,表示当前任务可以运行。0
,说明任务已经在运行中,当前实例不执行。同一会话获取新锁时,旧锁会自动释放:
SELECT GET_LOCK('lock_1', 10);
SELECT GET_LOCK('lock_2', 10); -- lock_1 会被释放
如果连接断开,锁会自动释放。因此,不适用于长时间锁定数据。
对于需要锁定多个表或跨多个 SQL 操作的事务,建议使用 LOCK TABLES
或 SELECT ... FOR UPDATE
。
特性 | 说明 |
---|---|
作用范围 | 会话级别 |
适用场景 | 并发控制、任务同步 |
一个会话持有多个锁 | ❌不支持 |
断开连接是否释放 | ✅自动释放 |
适合事务控制 | ❌不推荐 |
✅ 适用于:避免任务并发执行、控制资源访问
❌ 不适用于:需要锁定多个表或长时间持有锁的场景
如果你的需求是表级锁,建议使用 LOCK TABLES
;如果是行级锁,建议使用 SELECT ... FOR UPDATE
。