在MySQL中,有一种函数锁,类似于程序中的一个任务独占资源机制,当一个任务持有时,其他任务无法获得同名锁。在MySQL中,这种加锁机制是服务级别,允许一个客户端获取一个带有给定名称的锁,并且只有该客户端能够释放该锁,有利于控制并发访问数据操作。
函数锁
目前MySQL提供的用户级函数锁如下:
| 函数 | 描述 |
|---|---|
| GET_LOCK(str,timeout) | 获取一个命名锁【str锁命名,尝试获取锁的timeout超时秒时间,不是锁持有时间】 |
| IS_FREE_LOCK(str) | 指定的锁是否空闲,【str锁命名】 |
| IS_USED_LOCK(str) | 指定的锁是否正在使用中;如果为true,则返回连接标识符,【str锁命名】 |
| RELEASE_ALL_LOCKS() | 释放所有当前命名的锁 |
| RELEASE_LOCK() | 释放指定的锁 |
用法:
mysql>SELECT GET_LOCK('lockname',10);
mysql>SELECT IS_USED_LOCK('lockname');
mysql>SELECT IS_FREE_LOCK('lockname')
mysql>SELECT RELEASE_LOCK('lockname');
mysql>SELECT RELEASE_ALL_LOCKS();
- GET_LOCK:获得一个名为str的锁,并设置超时时间。超时时间负值表示无限超时。单一服务只能存在唯一命令的锁。如果成功获得锁,则返回1;如果尝试超时,则返回0;如果发生错误(例如内存不足或线程被mysqladmin kill杀死),则输出NULL。
- IS_FREE_LOCK:如果锁是空闲的(没有人使用锁),则返回1;如果锁正在使用中,则返回0;
- IS_USED_LOCK:检查名为str的锁是否正在使用中(即已锁定)。它将返回持有锁的客户端会话的连接标识符。否则,它将返回NULL。
- RELEASE_ALL_LOCKS:释放当前会话持有的所有命名锁,并返回释放的锁数(如果没有,则返回0)
- RELEASE_LOCK:释放由GET_lock()获得的字符串str命名的锁。如果锁已释放,则返回1;如果此线程未建立锁(在这种情况下,锁未释放),则返回0;如果指定的锁不存在,则返回NULL。
备足:此函数对于基于语句的复制是不安全的。如果在binlog_format设置为STATEMENT时使用此函数,则会记录一条警告
函数锁示例
示例1:获取和检查锁
3个窗口:窗口12获取锁函数,窗口11 获取锁函数,窗口10检查所持有情况。
- 1.窗口12获取成功锁;
- 2.窗口11再次获取lock1的锁,在10s内持续等待;
- 3.窗口10 查看线程Id,通过IS_USED_LOCK函数lock1锁已被窗口12持有;

示例2:链接断开,锁释放
- 1.窗口12退出会话;
- 2.窗口10 lock1锁返回NULL,已经释放;

示例3:释放锁
- 1.窗口12成功获取锁;
- 2.窗口10 提示获取锁线程Id;
- 3.窗口11 释放锁失败;
- 4.窗口10 提示获取锁线程Id;
- 5.窗口12 成功释放锁;
- 6.窗口10 显示锁不存在;

示例4:释放所有锁
- 1.窗口12 成功获取locak1和lock2锁;
- 2.窗口10 提示获取锁线程Id;
- 3.窗口12 成功释放所有锁;
- 4.窗口10 显示锁不存在;

示例5:实际案例
实现快递状态变更。快递订单设置4个状态:准备prepare,接单accept,配送progress,接受receive,完成finish.
构建表结构和数据:
mysql> CREATE TABLE delivery (
deliv_number CHAR(5) PRIMARY KEY,
deliv_status VARCHAR(50)
);
mysql> INSERT INTO delivery (deliv_number, deliv_status)
VALUES ('SH001', 'prepare'), ('SH002', 'accept'), ('SH003', 'accept');
``
使用快递编号,GET_LOCK方法来更新快递状态:
```sql
#获取锁
mysql> SELECT GET_LOCK('SH001', 10);
#更新对应状态数据
mysql> UPDATE delivery SET deliv_status ='receive' WHERE deliv_number ='SH001';
#释放锁
mysql> SELECT RELEASE_LOCK('SH001');
注意事项
- 锁的范围:GET_LOCK() 获取的锁是MySQL服务级别的,不是数据库或表级别的。这意味着它适用于跨数据库或表的操作保护。
- 锁的释放:必须显式地使用 RELEASE_LOCK() 释放锁,或者当持有锁的会话结束时锁会被自动释放。其他线程无法释放。
- 锁的性能:在高并发环境下,频繁地获取和释放锁可能会对性能产生影响。应该谨慎使用,并确保其必要性。
- 锁的可见性:锁是MySQL服务级别的,不是跨服务器的。如果应用是分布式的,并且需要在不同的 MySQL服务实例间共享锁,那么可能需要实现自己的分布式锁机制。
- 锁的名称:锁的名称在数据库实例中是唯一的,但是不同的 MySQL 实例之间锁的名称可以相同,它们互不干扰。
总结
函数锁虽然对于流程处理非常严谨,但对MySQL数据库性能来说,非常不友好。这种服务级别锁,因为获取锁之后,就是等待过程,所以消耗CPU资源。大量的函数锁,会占有CPU资源(信号量、自旋锁),导致无法处理其他任务。实际生产中,不建议使用。
参考:
https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




