golang,go,博客,开源,编程

mysql GET_LOCK

Published on with 0 views and 0 comments

MySQL GET_LOCK() 详解

GET_LOCK() 是 MySQL 提供的一个 用户级锁函数,用于在会话(Session)级别创建一个 命名互斥锁(named mutex lock),用于** 同步多个会话**,防止资源冲突。


1. GET_LOCK() 语法

GET_LOCK(lock_name, timeout)

参数说明

  • lock_name:要创建的锁的名称,必须是字符串(最大 64 字节)。
  • timeout:等待锁的时间()。
    • 0 代表立即返回,不等待。
    • -1 代表无限等待,直到获取到锁。
    • 其他正整数表示最多等待多少秒,如果超时则返回 0

返回值

返回值说明
1获取锁成功
0获取锁失败(可能因为超时或锁已被其他会话占用)
NULL发生错误(如连接断开)

2. GET_LOCK() 示例

2.1 获取锁

SELECT GET_LOCK('my_lock', 10);
  • 若锁 未被其他会话占用,则返回 1(成功)。
  • 若锁 已被占用,最多等 10 秒,如果还没释放,则返回 0

2.2 释放锁

SELECT RELEASE_LOCK('my_lock');
返回值说明
1释放成功
0释放失败(该锁不存在或不是当前会话持有的锁)
NULL发生错误

⚠️ 只有持有锁的会话才能释放锁!其他会话无法释放该锁。


2.3 立即获取锁

SELECT GET_LOCK('my_lock', 0);
  • 若锁可用,则立即返回 1,表示成功获取。
  • 若锁已被占用,则立即返回 0,不会等待。

2.4 无限等待获取锁

SELECT GET_LOCK('my_lock', -1);
  • 若锁可用,则立即返回 1,表示成功获取。
  • 若锁已被占用,则 一直等待 直到锁释放。

3. 多个会话使用 GET_LOCK()

3.1 会话 A 先获取锁

SELECT GET_LOCK('my_lock', 10);

✅ 返回 1,获取锁成功。

3.2 会话 B 试图获取同一个锁

SELECT GET_LOCK('my_lock', 5);

⏳ 会话 B 需要等待 最多 5 秒

  • 如果 会话 A 释放了锁,B 会获取到锁并返回 1
  • 如果 5 秒内 A 没释放,B 返回 0(超时)。

3.3 会话 A 释放锁

SELECT RELEASE_LOCK('my_lock');

✅ 返回 1,表示锁已释放。

  • 如果 会话 B 正在等待,它会立即获取到锁并返回 1

4. 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 TABLESadvisory lock

锁不会跨会话

如果会话 A 获取了 GET_LOCK('my_lock', 10),然后会话 A 断开连接,MySQL 会自动释放该锁,其他会话可以重新获取它。


5. GET_LOCK() vs LOCK TABLES vs advisory locks

方式作用范围是否会自动释放适用场景
GET_LOCK()会话级别仅限当前会话同步不同 SQL 任务
LOCK TABLES数据库表级断开连接会释放需要锁定整个表的事务
advisory lock(pg_advisory_lockin PostgreSQL)进程级需要手动释放多进程同步

6. 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,说明任务已经在运行中,当前实例不执行。

7. 可能的坑

同一会话只能持有一个锁

同一会话获取新锁时,旧锁会自动释放:

SELECT GET_LOCK('lock_1', 10);
SELECT GET_LOCK('lock_2', 10); -- lock_1 会被释放

锁的作用范围仅限当前 MySQL 连接

如果连接断开,锁会自动释放。因此,不适用于长时间锁定数据

不适用于复杂事务

对于需要锁定多个表或跨多个 SQL 操作的事务,建议使用 LOCK TABLESSELECT ... FOR UPDATE


总结

特性说明
作用范围会话级别
适用场景并发控制、任务同步
一个会话持有多个锁不支持
断开连接是否释放自动释放
适合事务控制不推荐

适用于:避免任务并发执行、控制资源访问
不适用于:需要锁定多个表或长时间持有锁的场景

如果你的需求是表级锁,建议使用 LOCK TABLES;如果是行级锁,建议使用 SELECT ... FOR UPDATE


标题:mysql GET_LOCK
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/02/28/1740728625849.html
联系:scotttu@163.com