30 | 如何使用Redis实现分布式锁?
蒋德钧
该思维导图由 AI 生成,仅供参考
你好,我是蒋德钧。
上节课,我提到,在应对并发问题时,除了原子操作,Redis 客户端还可以通过加锁的方式,来控制并发写操作对共享数据的修改,从而保证数据的正确性。
但是,Redis 属于分布式系统,当有多个客户端需要争抢锁时,我们必须要保证,这把锁不能是某个客户端本地的锁。否则的话,其它客户端是无法访问这把锁的,当然也就不能获取这把锁了。
所以,在分布式系统中,当有多个客户端需要获取锁时,我们需要分布式锁。此时,锁是保存在一个共享存储系统中的,可以被多个客户端共享访问和获取。
Redis 本身可以被多个客户端共享访问,正好就是一个共享存储系统,可以用来保存分布式锁。而且 Redis 的读写性能高,可以应对高并发的锁操作场景。所以,这节课,我就来和你聊聊如何基于 Redis 实现分布式锁。
我们日常在写程序的时候,经常会用到单机上的锁,你应该也比较熟悉了。而分布式锁和单机上的锁既有相似性,但也因为分布式锁是用在分布式场景中,所以又具有一些特殊的要求。
所以,接下来,我就先带你对比下分布式锁和单机上的锁,找出它们的联系与区别,这样就可以加深你对分布式锁的概念和实现要求的理解。
单机上的锁和分布式锁的联系与区别
我们先来看下单机上的锁。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
分布式锁是保证数据正确性的重要工具,而Redis作为共享存储系统,可以用来实现分布式锁。在单个Redis节点上,可以使用SETNX和DEL命令组合来实现加锁和释放锁操作,但存在异常情况下锁无法释放和误释放锁的风险。为了解决这些问题,可以给锁变量设置过期时间和唯一标识,以确保锁的可靠性。通过Lua脚本可以保证加锁和释放锁操作的原子性。然而,单个Redis节点的故障可能导致锁的丢失,因此需要基于多个Redis节点实现分布式锁来提高可靠性。Redlock算法是一种有效的解决方案,通过让客户端和多个独立的Redis实例依次请求加锁来保证分布式锁的正常工作。在实际的业务应用中,如果想要提升分布式锁的可靠性,可以通过Redlock算法来实现。文章详细介绍了Redlock算法的执行步骤和实现方式,以及基于单个Redis实例实现分布式锁时需要满足的条件。通过本文的总结,读者可以快速了解分布式锁的重要性以及基于Redis实现分布式锁的方式和技术特点。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 核心技术与实战》,新⼈⾸单¥68
《Redis 核心技术与实战》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(78)
- 最新
- 精选
- 小喵喵请教下老师文章中说的客户端,这个客户端指的是什么?比如浏览器下单,app下单,这个浏览器,app就是客户端吗?
作者回复: 客户端是指向Redis发送读写请求的应用程序。你举的例子中,app的下单请求先是被发送到后端的服务器上,一般来说,服务器上还有业务程序先解析了app的请求,然后再向Redis发送读写请求,在这种情况下,服务器上的业务程序就是Redis的客户端。而app、浏览器算是服务器上业务程序的客户端。
2020-11-137 - Kaito是否可以使用 SETNX + EXPIRE 来完成加锁操作? 不可以这么使用。使用 2 个命令无法保证操作的原子性,在异常情况下,加锁结果会不符合预期。异常情况主要分为以下几种情况: 1、SETNX 执行成功,执行 EXPIRE 时由于网络问题设置过期失败 2、SETNX 执行成功,此时 Redis 实例宕机,EXPIRE 没有机会执行 3、SETNX 执行成功,客户端异常崩溃,EXPIRE 没有机会执行 如果发生以上情况,并且客户端在释放锁时发生异常,没有正常释放锁,那么这把锁就会一直无法释放,其他线程都无法再获得锁。 下面说一下关于 Redis 分布式锁可靠性的问题。 使用单个 Redis 节点(只有一个master)使用分布锁,如果实例宕机,那么无法进行锁操作了。那么采用主从集群模式部署是否可以保证锁的可靠性? 答案是也很难保证。如果在 master 上加锁成功,此时 master 宕机,由于主从复制是异步的,加锁操作的命令还未同步到 slave,此时主从切换,新 master 节点依旧会丢失该锁,对业务来说相当于锁失效了。 所以 Redis 作者才提出基于多个 Redis 节点(master节点)的 Redlock 算法,但这个算法涉及的细节很多,作者在提出这个算法时,业界的分布式系统专家还与 Redis 作者发生过一场争论,来评估这个算法的可靠性,争论的细节都是关于异常情况可能导致 Redlock 失效的场景,例如加锁过程中客户端发生了阻塞、机器时钟发生跳跃等等。 感兴趣的可以看下这篇文章,详细介绍了争论的细节,以及 Redis 分布式锁在各种异常情况是否安全的分析,收益会非常大:http://zhangtielei.com/posts/blog-redlock-reasoning.html。 简单总结,基于 Redis 使用分布锁的注意点: 1、使用 SET $lock_key $unique_val EX $second NX 命令保证加锁原子性,并为锁设置过期时间 2、锁的过期时间要提前评估好,要大于操作共享资源的时间 3、每个线程加锁时设置随机值,释放锁时判断是否和加锁设置的值一致,防止自己的锁被别人释放 4、释放锁时使用 Lua 脚本,保证操作的原子性 5、基于多个节点的 Redlock,加锁时超过半数节点操作成功,并且获取锁的耗时没有超过锁的有效时间才算加锁成功 6、Redlock 释放锁时,要对所有节点释放(即使某个节点加锁失败了),因为加锁时可能发生服务端加锁成功,由于网络问题,给客户端回复网络包失败的情况,所以需要把所有节点可能存的锁都释放掉 7、使用 Redlock 时要避免机器时钟发生跳跃,需要运维来保证,对运维有一定要求,否则可能会导致 Redlock 失效。例如共 3 个节点,线程 A 操作 2 个节点加锁成功,但其中 1 个节点机器时钟发生跳跃,锁提前过期,线程 B 正好在另外 2 个节点也加锁成功,此时 Redlock 相当于失效了(Redis 作者和分布式系统专家争论的重要点就在这) 8、如果为了效率,使用基于单个 Redis 节点的分布式锁即可,此方案缺点是允许锁偶尔失效,优点是简单效率高 9、如果是为了正确性,业务对于结果要求非常严格,建议使用 Redlock,但缺点是使用比较重,部署成本高2020-10-2868337
- Darren其实分布式锁选择Etcd的话,可能会更好。 Etcd 支持以下功能,正是依赖这些功能来实现分布式锁的: 1、Lease 即租约机制(TTL,Time To Live)机制: Etcd 可以为存储的 KV 对设置租约,当租约到期,KV 将失效删除;同时也支持续约,即 KeepAlive; Redis在这方面很难实现,一般假设通过SETNX设置的时间10S,如果发生网络抖动,万一业务执行超过10S,此时别的线程就能回去到锁; 2、Revision 机制: Etcd 每个 key 带有一个 Revision 属性值,每进行一次事务对应的全局 Revision 值都会加一,因此每个 key 对应的 Revision 属性值都是全局唯一的。通过比较 Revision 的大小就可以知道进行写操作的顺序。 在实现分布式锁时,多个程序同时抢锁,根据 Revision 值大小依次获得锁,可以避免 “惊群效应”,实现公平锁; Redis很难实现公平锁,而且在某些情况下,也会产生 “惊群效应”; 3、Prefix 即前缀机制,也称目录机制: Etcd 可以根据前缀(目录)获取该目录下所有的 key 及对应的属性(包括 key, value 以及 revision 等); Redis也可以的,使用keys命令或者scan,生产环境一定要使用scan; 4、Watch 机制: Etcd Watch 机制支持 Watch 某个固定的 key,也支持 Watch 一个目录(前缀机制),当被 Watch 的 key 或目录发生变化,客户端将收到通知; Redis只能通过客户端定时轮训的形式去判断key是否存在;2020-10-28876
- 每天晒白牙配合这篇文章看,效果更佳 https://mp.weixin.qq.com/s/2P2-ujcdet9cUycokfyjHQ 这篇是早晨通勤坐地铁开了一个番茄钟看完的,番茄钟开启学霸模式,将这些学习和读书的app加入白名单,在一个番茄钟周期内就不能打开那些未加入白名单的app,可以防止刷耗精力的app,工作时也经常用,效果不错 如果自己能总结出来并给别人讲明白,就像课代表一样,每篇文章的留言都非常优秀,每次必看,这个知识点就掌握了,能用到工作实战上,那就锦上添花了2020-10-30419
- 凯文小猪这里老师漏了一点 就是session timeout处理 在分布式锁的场景中就是: 一个key过期了 但是代码还没处理完 此时就发生了重复加锁的问题。 通常我们有两种方式处理: 1. 设置看门狗 也就是redision的处理方式 2. 设置状态机 由最后的业务层来做代码回溯2021-06-10311
- Geek_lijia关于分布式锁,两个大神的争论,我站Martin这边。 https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html http://antirez.com/news/1012022-01-135
- 喰对于给锁设置过期时间,即使再怎么预估也很难保证线程在锁有效时间内完成操作。而且预估时间设置的过大也会影响系统性能,所以可以使用一个守护线程进行续租。2021-07-025
- COLDLY老师,请问锁的过期时间怎么设置,会不会因为客户端执行业务操作时耗时太久超过过期时间,导致锁过期被释放了2021-01-0513
- 范闲不可以。这两个命令不是原子的。发生异常的时候,可能会有正常的加锁结果。 分布式锁: 1.唯一性 2.原子性 3.可重入 Redlock Redisson zookeeper etcd都是业界常用的分布式锁方案。2020-12-0313
- 漂泊者及其影子//释放锁 比较unique_value是否相等,避免误释放 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end 释放锁为什么不能使用delete操作?2020-10-30123
收起评论