码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Redis的分布式锁问题(九)Redis + Lua 脚本实现分布式锁


    Redis的分布式锁问题(九)Redis + Lua 脚本实现分布式锁

    上集回顾

    Lua的简单介绍 

    redis调用函数  

    set name jack 

    set name Rose,再执行get name 

    redis的 EVAL 命令 

    Lua脚本解决unLock业务流程 

    代码实现 

    unLock.lua 

    RedisTemplate调用Lua脚本的API 

    Lua 解决unLock问题


    Redis的分布式锁问题(九)Redis + Lua 脚本实现分布式锁

    上集回顾

    Redis的分布式锁问题(八)基于Redis的分布式锁_面向鸿蒙编程的博客-CSDN博客icon-default.png?t=M85Bhttps://blog.csdn.net/weixin_43715214/article/details/127967364我们在上一个章节中解决了“分布式锁误删问题”,改进后的代码逻辑如下所示:

    但是这仍然不是最佳的实现方案,它在极端的情况下还是会发生问题!

    1. public void unlock() {
    2. // 获取线程标示
    3. String threadId = ID_PREFIX + Thread.currentThread().getId();
    4. // 获取锁中的标示
    5. String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
    6. // 判断标示是否一致
    7. if(threadId.equals(id)) {
    8. // 释放锁
    9. stringRedisTemplate.delete(KEY_PREFIX + name);
    10. }
    11. }

    如上述代码,如果“判断锁标识”和“释放锁”,之间发生了阻塞呢?(JVM触发FULL GC)

    那么之前一节所讲的“锁误删”就有可能发生!!!

    所以,我们的解决思路是要保证这两个操作的原子性!

    我们可以使用Redis的事务+乐观锁来解决这个问题,但是这样子做非常复杂!这里我们使用 Lua 脚本来实现分布式锁

    Lua的简单介绍 

    Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。 

    Lua 教程 | 菜鸟教程 (runoob.com)icon-default.png?t=M85Bhttps://www.runoob.com/lua/lua-tutorial.html

    1. if(0)
    2. then
    3. print("0 为 true")
    4. end

    redis调用函数  

    set name jack 

    set name Rose,再执行get name 

    redis的 EVAL 命令 

    如果脚本中的key、value不想写死,可以作为参数传递。key类型参数会放入KEYS数组,其它参数会放入ARGV数组,在脚本中可以从KEYS和ARGV数组获取这些参数:

    在Lua中,数组的下标是从1开始的!!!

    Lua脚本解决unLock业务流程 

    1. 获取锁中的线程标示
    2. 判断是否与指定的标示(当前线程标示)一致
    3. 如果一致则释放锁(删除)
    4. 如果不一致则什么都不做

    代码实现 

    unLock.lua 

    1. -- 获取锁标识,是否与当前线程一致?
    2. if(redis.call('get', KEYS[1]) == ARGV[1]) then
    3. -- 一致,删除
    4. return redis.call('del', KEYS[1])
    5. end
    6. -- 不一致,直接返回
    7. return 0

    RedisTemplate调用Lua脚本的API 

    Lua 解决unLock问题

    1. /**
    2. * redis的分布式锁
    3. * 实现ILock接口
    4. */
    5. public class SimpleRedisLock implements ILock {
    6. // 不同的业务有不同的锁名称
    7. private String name;
    8. private StringRedisTemplate stringRedisTemplate;
    9. private static final String KEY_PREFIX = "lock:";
    10. private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
    11. // DefaultRedisScript,
    12. private static final DefaultRedisScript UNLOCK_SCRIPT;
    13. public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
    14. this.name = name;
    15. this.stringRedisTemplate = stringRedisTemplate;
    16. }
    17. // 初始化 UNLOCK_SCRIPT,用静态代码块的方式,一加载SimpleRedisLock有会加载unlock.lua
    18. // 避免每次调unLock() 才去加载,提升性能!!!
    19. static {
    20. UNLOCK_SCRIPT = new DefaultRedisScript<>();
    21. // setLocation() 设置脚本位置
    22. UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
    23. // 返回值类型
    24. UNLOCK_SCRIPT.setResultType(Long.class);
    25. }
    26. /**
    27. * 获取锁
    28. */
    29. @Override
    30. public boolean tryLock(long timeoutSec) {
    31. // 获取线程标示
    32. String threadId = ID_PREFIX + Thread.currentThread().getId();
    33. // 获取锁
    34. // set lock thread1 nx ex 10
    35. // nx : setIfAbsent(如果不存在) , ex : timeoutSec(秒)
    36. Boolean success = stringRedisTemplate.opsForValue()
    37. .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
    38. // 自动拆箱(Boolean -> boolean)!!!可能有风险
    39. return Boolean.TRUE.equals(success);
    40. }
    41. /**
    42. * 解决判断(锁标识、释放锁)这两个动作,之间产生阻塞!!!
    43. * JVM的 FULL GC
    44. * 要让这两个动作具有原子性
    45. */
    46. @Override
    47. public void unlock() {
    48. // 调用lua脚本
    49. stringRedisTemplate.execute(
    50. UNLOCK_SCRIPT,
    51. Collections.singletonList(KEY_PREFIX + name),
    52. ID_PREFIX + Thread.currentThread().getId());
    53. }
    54. }
  • 相关阅读:
    GEE:计算NDVI时间序列和谐波拟合曲线之间的残差时间序列
    ventoy安装操作系统
    冒泡排序给cpu干懵了 哈哈 还有希尔排序 算法补充(学习笔记)
    mysql 字符串拼接的几种方式
    【工业设计】设计应注意技术、情感需求和技术发展
    主机ping不通虚拟机,虚拟机可以ping同主机
    【mcuclub】时钟模块DS1302
    Docker/Linux 安装Mysql
    Python学习之CSDN21天学习挑战赛计划之4
    【Vue】悬浮窗和聚焦登录组件经验总结
  • 原文地址:https://blog.csdn.net/weixin_43715214/article/details/127982757
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号