什么是分布式锁

分布式锁:不同进程必须以互斥方式使用共享资源的一种锁方法实现。

实现分布式锁的基础

互斥。任何时刻,只有一个客户端持有锁。

无死锁。最终总是有可能获得锁,即使持有锁的客户端已经崩溃。

单个 Redis 分布式锁实现

上锁

上锁需要考虑俩点

  • 原子性
  • 锁能自动释放

首先要考虑持有锁的客户端挂掉后,锁一直得不到释放的情况,这时候需要借助 Redis 的 Expire 自动过期功能,在上锁时设置一个过期时间,锁到期后自动释放,就可以避免死锁的状况。

但问题来了,现在有个难题,上锁和设置过期时间是俩个步骤,如果用 Redis的 SETNX+EXPIRE 俩条指令,这不能保证上锁这一步骤是互斥的。假设现在有个线程A 已经执行完 SETNX,正在执行 EXPIRE 指令,Redis 宕机了,这锁就有概率变成死锁了。

为了避免死锁的产生,我们要将上锁步骤设置为原子性。这里有俩种方式实现:

  • Lua
  • SET EX NX

使用上述俩种,都能保证上锁是原子性的,死锁问题解决。

持有锁

上锁时设置了锁的过期时间。假如锁已经过期释放了,但业务还没执行完,这时候另外一客户端得到锁。此时临界区资源同时被俩进程使用,违背互斥。

为了解决这一问题,持有锁的客户端得定期去延长锁的过期时间。但如果去延期锁呢?直接 SET KEY EXPIRE 的话,其他进程也可以随意修改锁的过期时间,不安全。

所以需要一个能够一个 UUID 证明是锁的持有者。在上锁时,将锁的 value 设置为该线程的 UUID;在延期锁时,先获取锁的 value 对比该线程持有的 UUID,一致后才能延期。

释放锁

释放锁的时候也需要先证明是锁的持有者,然后再执行 delete 操作。证明+释放操作是俩步的,我们也要将其变成原子性的,一般使用 Lua 脚本执行证明+释放锁操作。

多 Redis 实例的分布式锁实现

现在我们已经完成一个对于单个始终可用的 Redis 实例的分布式锁版本。下面我们来分布式锁拓展到分布式 Redis 系统中。

我们可以将基于单实例的 Redis 分布式锁思路应用到多 Redis 环境里来。在示例中,我们有 5 个 Redis Master 实例,我们需要确保在这些 Redis 实例中锁信息是一致的。

为了获取锁,客户端执行以下操作:

  1. 以毫秒为单位获取当前时间。
  2. 尝试按顺序获取所有 N 个实例中的锁,在所有实例中使用相同的键名和随机值。在步骤 2 中,在每个实例中设置锁时,客户端以一个比锁的自动释放时间更小的超时时间来获取锁。例如,如果锁的自动释放时间为 10 秒,则获取锁的超时时间应该设置为 5 ~ 50 毫秒范围内。这可以防止客户端长时间处于阻塞状态,尝试与已关闭的 Redis 节点通信:如果实例不可用,我们应该尽快尝试与下一个实例通信。
  3. 客户端通过从当前时间减去在步骤 1 中获得的时间戳来计算获取锁所经过的时间。当且仅当客户端能够在大多数实例(至少 3 个)中获取锁,并且获取锁所用的总时间小于锁有效期时,锁被视为已获取。
  4. 如果获取了锁,则其有效时间被视为初始有效时间减去经过的时间,如步骤 3 中计算的那样。
  5. 如果客户端由于某种原因(无法锁定 N/2+1 个实例或有效时间为负)未能获取锁,那么将尝试解锁所有 Redis实例(甚至是客户端认为无法获取锁的实例)

项目实现

以下项目是基于上述思路实现分布锁的成功案例,我们在开发过程中可以上手即用,不必自己重复造轮子。

参考链接:

https://redis.io/docs/manual/patterns/distributed-locks/

https://juejin.cn/post/6936956908007850014

最新文章

  1. WP7 手机软件纪念 - 稍后读软件
  2. Nagios学习实践系列——配置研究[监控当前服务器]
  3. OpenCV在VS2005下的配置
  4. lamp环境搭建(ubuntu)
  5. 机器学习中的数学(3)-模型组合(Model Combining)之Boosting与Gradient Boosting
  6. 学习php 韩顺平 数据类型 三元运算,字符串运算类型运算
  7. 转:传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确 .
  8. zoj 3829 Known Notation
  9. 转载--C语言运算符优先级和口诀
  10. 移植QT到ZedBoard(制作运行库镜像) 交叉编译 分类: ubuntu shell ZedBoard OpenCV 2014-11-08 18:49 219人阅读 评论(0) 收藏
  11. Plsql工具单步调试 存储过程或是 函数(oracle数据库)-留着自己用的
  12. C#中的序列化与反序列化
  13. 自定义滚动条 niceScroll 配置参数
  14. 异构数据源海量数据交换工具-Taobao DataX 下载和使用
  15. Problem and Solution Code Snippets
  16. Python中的单例模式——装饰器实现剖析
  17. 团体程序设计天梯赛(CCCC) L3014 周游世界 BFS证明
  18. [转]Redis内部数据结构详解-sds
  19. java调用url
  20. mfc 动态创建EDIT控件

热门文章

  1. 知识图谱实体对齐1:基于平移(translation)的方法
  2. 离线安装chrome浏览器的postman插件
  3. Docker容器技术基础
  4. Effective java 总结
  5. Redis高可用之主从复制原理演进分析
  6. Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution
  7. 【NOI2016】 循环之美 题解
  8. C语言两个升序递增链表逆序合并为一个降序递减链表,并去除重复元素
  9. SpringBoot自动配置(装配)流程
  10. 如何用webgl(three.js)搭建一个3D库房,3D仓库3D码头,3D集装箱,车辆定位,叉车定位可视化孪生系统——第十五课