暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

MySQL 用户级GET_LOCK函数锁

原创 CuiHulong 2024-10-28
1019

在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持有;
    image.png

示例2:链接断开,锁释放

  • 1.窗口12退出会话;
  • 2.窗口10 lock1锁返回NULL,已经释放;
    image.png

示例3:释放锁

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

示例4:释放所有锁

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

示例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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论