Redis 核心技术与实战
蒋德钧
中科院计算所副研究员
81696 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 53 讲
开篇词 (1讲)
实践篇 (28讲)
Redis 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

30 | 如何使用Redis实现分布式锁?

EX/PX选项
NX选项
区分不同客户端的锁操作
解决方法:设置过期时间
释放锁操作
加锁条件
执行步骤
Lua脚本实现释放锁操作
SET命令
潜在风险
DEL命令
SETNX命令
Redlock算法
加锁操作
键值对保存锁变量
基于多个Redis节点实现高可靠的分布式锁
基于单个Redis节点实现分布式锁
可靠性
原子性
基于Redis实现
要求
释放锁操作
加锁操作
分布式锁
单机上的锁
每课一问
分布式锁
如何使用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
立即购买
登录 后留言

全部留言(78)

  • 最新
  • 精选
  • 小喵喵
    请教下老师文章中说的客户端,这个客户端指的是什么?比如浏览器下单,app下单,这个浏览器,app就是客户端吗?

    作者回复: 客户端是指向Redis发送读写请求的应用程序。你举的例子中,app的下单请求先是被发送到后端的服务器上,一般来说,服务器上还有业务程序先解析了app的请求,然后再向Redis发送读写请求,在这种情况下,服务器上的业务程序就是Redis的客户端。而app、浏览器算是服务器上业务程序的客户端。

    2020-11-13
    7
  • 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-28
    68
    337
  • 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-28
    8
    76
  • 每天晒白牙
    配合这篇文章看,效果更佳 https://mp.weixin.qq.com/s/2P2-ujcdet9cUycokfyjHQ 这篇是早晨通勤坐地铁开了一个番茄钟看完的,番茄钟开启学霸模式,将这些学习和读书的app加入白名单,在一个番茄钟周期内就不能打开那些未加入白名单的app,可以防止刷耗精力的app,工作时也经常用,效果不错 如果自己能总结出来并给别人讲明白,就像课代表一样,每篇文章的留言都非常优秀,每次必看,这个知识点就掌握了,能用到工作实战上,那就锦上添花了
    2020-10-30
    4
    19
  • 凯文小猪
    这里老师漏了一点 就是session timeout处理 在分布式锁的场景中就是: 一个key过期了 但是代码还没处理完 此时就发生了重复加锁的问题。 通常我们有两种方式处理: 1. 设置看门狗 也就是redision的处理方式 2. 设置状态机 由最后的业务层来做代码回溯
    2021-06-10
    3
    11
  • Geek_lijia
    关于分布式锁,两个大神的争论,我站Martin这边。 https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html http://antirez.com/news/101
    2022-01-13
    5
  • 对于给锁设置过期时间,即使再怎么预估也很难保证线程在锁有效时间内完成操作。而且预估时间设置的过大也会影响系统性能,所以可以使用一个守护线程进行续租。
    2021-07-02
    5
  • COLDLY
    老师,请问锁的过期时间怎么设置,会不会因为客户端执行业务操作时耗时太久超过过期时间,导致锁过期被释放了
    2021-01-05
    1
    3
  • 范闲
    不可以。这两个命令不是原子的。发生异常的时候,可能会有正常的加锁结果。 分布式锁: 1.唯一性 2.原子性 3.可重入 Redlock Redisson zookeeper etcd都是业界常用的分布式锁方案。
    2020-12-03
    1
    3
  • 漂泊者及其影子
    //释放锁 比较unique_value是否相等,避免误释放 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end 释放锁为什么不能使用delete操作?
    2020-10-30
    12
    3
收起评论
显示
设置
留言
78
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部