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

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

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