当前位置: 首页 > news >正文

cms三合一网站源码软件定制开发一般多少钱

cms三合一网站源码,软件定制开发一般多少钱,申请免费网址,网站设计流程是作者 | Kaito 来源 | 水滴与银弹阅读本文大约需要 20 分钟。大家好#xff0c;我是 Kaito。这篇文章我想和你聊一聊#xff0c;关于 Redis 分布式锁的「安全性」问题。Redis 分布式锁的话题#xff0c;很多文章已经写烂了#xff0c;我为什么还要写这篇文章呢#xff1f;因… 作者 | Kaito 来源 | 水滴与银弹阅读本文大约需要 20 分钟。大家好我是 Kaito。这篇文章我想和你聊一聊关于 Redis 分布式锁的「安全性」问题。Redis 分布式锁的话题很多文章已经写烂了我为什么还要写这篇文章呢因为我发现网上 99% 的文章并没有把这个问题真正讲清楚。导致很多读者看了很多文章依旧云里雾里。例如下面这些问题你能清晰地回答上来吗基于 Redis 如何实现一个分布式锁Redis 分布式锁真的安全吗Redis 的 Redlock 有什么问题一定安全吗业界争论 Redlock到底在争论什么哪种观点是对的分布式锁到底用 Redis 还是 Zookeeper实现一个有「容错性」的分布式锁都需要考虑哪些问题这篇文章我就来把这些问题彻底讲清楚。读完这篇文章你不仅可以彻底了解分布式锁还会对「分布式系统」有更加深刻的理解。文章有点长但干货很多希望你可以耐心读完。为什么需要分布式锁在开始讲分布式锁之前有必要简单介绍一下为什么需要分布式锁与分布式锁相对应的是「单机锁」我们在写多线程程序时避免同时操作一个共享变量产生数据问题通常会使用一把锁来「互斥」以保证共享变量的正确性其使用范围是在「同一个进程」中。如果换做是多个进程需要同时操作一个共享资源如何互斥呢例如现在的业务应用通常都是微服务架构这也意味着一个应用会部署多个进程那这多个进程如果需要修改 MySQL 中的同一行记录时为了避免操作乱序导致数据错误此时我们就需要引入「分布式锁」来解决这个问题了。想要实现分布式锁必须借助一个外部系统所有进程都去这个系统上申请「加锁」。而这个外部系统必须要实现「互斥」的能力即两个请求同时进来只会给一个进程返回成功另一个返回失败或等待。这个外部系统可以是 MySQL也可以是 Redis 或 Zookeeper。但为了追求更好的性能我们通常会选择使用 Redis 或 Zookeeper 来做。下面我就以 Redis 为主线由浅入深带你深度剖析一下分布式锁的各种「安全性」问题帮你彻底理解分布式锁。分布式锁怎么实现我们从最简单的开始讲起。想要实现分布式锁必须要求 Redis 有「互斥」的能力我们可以使用 SETNX 命令这个命令表示SET if Not eXists即如果 key 不存在才会设置它的值否则什么也不做。两个客户端进程可以执行这个命令达到互斥就可以实现一个分布式锁。客户端 1 申请加锁加锁成功127.0.0.1:6379 SETNX lock 1 (integer) 1     // 客户端1加锁成功客户端 2 申请加锁因为它后到达加锁失败127.0.0.1:6379 SETNX lock 1 (integer) 0     // 客户端2加锁失败此时加锁成功的客户端就可以去操作「共享资源」例如修改 MySQL 的某一行数据或者调用一个 API 请求。操作完成后还要及时释放锁给后来者让出操作共享资源的机会。如何释放锁呢也很简单直接使用 DEL 命令删除这个 key 即可127.0.0.1:6379 DEL lock // 释放锁 (integer) 1这个逻辑非常简单整体的路程就是这样但是它存在一个很大的问题当客户端 1 拿到锁后如果发生下面的场景就会造成「死锁」程序处理业务逻辑异常没及时释放锁进程挂了没机会释放锁这时这个客户端就会一直占用这个锁而其它客户端就「永远」拿不到这把锁了。怎么解决这个问题呢如何避免死锁我们很容易想到的方案是在申请锁时给这把锁设置一个「租期」。在 Redis 中实现时就是给这个 key 设置一个「过期时间」。这里我们假设操作共享资源的时间不会超过 10s那么在加锁时给这个 key 设置 10s 过期即可127.0.0.1:6379 SETNX lock 1    // 加锁 (integer) 1 127.0.0.1:6379 EXPIRE lock 10  // 10s后自动过期 (integer) 1这样一来无论客户端是否异常这个锁都可以在 10s 后被「自动释放」其它客户端依旧可以拿到锁。但这样真的没问题吗还是有问题。现在的操作加锁、设置过期是 2 条命令有没有可能只执行了第一条第二条却「来不及」执行的情况发生呢例如SETNX 执行成功执行 EXPIRE 时由于网络问题执行失败SETNX 执行成功Redis 异常宕机EXPIRE 没有机会执行SETNX 执行成功客户端异常崩溃EXPIRE 也没有机会执行总之这两条命令不能保证是原子操作一起成功就有潜在的风险导致过期时间设置失败依旧发生「死锁」问题。怎么办在 Redis 2.6.12 版本之前我们需要想尽办法保证 SETNX 和 EXPIRE 原子性执行还要考虑各种异常情况如何处理。但在 Redis 2.6.12 之后Redis 扩展了 SET 命令的参数用这一条命令就可以了// 一条命令保证原子性执行 127.0.0.1:6379 SET lock 1 EX 10 NX OK这样就解决了死锁问题也比较简单。我们再来看分析下它还有什么问题试想这样一种场景客户端 1 加锁成功开始操作共享资源客户端 1 操作共享资源的时间「超过」了锁的过期时间锁被「自动释放」客户端 2 加锁成功开始操作共享资源客户端 1 操作共享资源完成释放锁但释放的是客户端 2 的锁看到了么这里存在两个严重的问题锁过期客户端 1 操作共享资源耗时太久导致锁被自动释放之后被客户端 2 持有释放别人的锁客户端 1 操作共享资源完成后却又释放了客户端 2 的锁导致这两个问题的原因是什么我们一个个来看。第一个问题可能是我们评估操作共享资源的时间不准确导致的。例如操作共享资源的时间「最慢」可能需要 15s而我们却只设置了 10s 过期那这就存在锁提前过期的风险。过期时间太短那增大冗余时间例如设置过期时间为 20s这样总可以了吧这样确实可以「缓解」这个问题降低出问题的概率但依旧无法「彻底解决」问题。为什么原因在于客户端在拿到锁之后在操作共享资源时遇到的场景有可能是很复杂的例如程序内部发生异常、网络请求超时等等。既然是「预估」时间也只能是大致计算除非你能预料并覆盖到所有导致耗时变长的场景但这其实很难。有什么更好的解决方案吗别急关于这个问题我会在后面详细来讲对应的解决方案。我们继续来看第二个问题。第二个问题在于一个客户端释放了其它客户端持有的锁。想一下导致这个问题的关键点在哪重点在于每个客户端在释放锁时都是「无脑」操作并没有检查这把锁是否还「归自己持有」所以就会发生释放别人锁的风险这样的解锁流程很不「严谨」如何解决这个问题呢锁被别人释放怎么办?解决办法是客户端在加锁时设置一个只有自己知道的「唯一标识」进去。例如可以是自己的线程 ID也可以是一个 UUID随机且唯一这里我们以 UUID 举例// 锁的VALUE设置为UUID 127.0.0.1:6379 SET lock $uuid EX 20 NX OK这里假设 20s 操作共享时间完全足够先不考虑锁自动过期的问题。之后在释放锁时要先判断这把锁是否还归自己持有伪代码可以这么写// 锁是自己的才释放 if redis.get(lock)  $uuid:redis.del(lock)这里释放锁使用的是 GET DEL 两条命令这时又会遇到我们前面讲的原子性问题了。客户端 1 执行 GET判断锁是自己的客户端 2 执行了 SET 命令强制获取到锁虽然发生概率比较低但我们需要严谨地考虑锁的安全性模型客户端 1 执行 DEL却释放了客户端 2 的锁由此可见这两个命令还是必须要原子执行才行。怎样原子执行呢Lua 脚本。我们可以把这个逻辑写成 Lua 脚本让 Redis 来执行。因为 Redis 处理每一个请求是「单线程」执行的在执行一个 Lua 脚本时其它请求必须等待直到这个 Lua 脚本处理完成这样一来GET DEL 之间就不会插入其它命令了。安全释放锁的 Lua 脚本如下// 判断锁是自己的才释放 if redis.call(GET,KEYS[1])  ARGV[1] thenreturn redis.call(DEL,KEYS[1]) elsereturn 0 end好了这样一路优化整个的加锁、解锁的流程就更「严谨」了。这里我们先小结一下基于 Redis 实现的分布式锁一个严谨的的流程如下加锁SET lock_key $unique_id EX $expire_time NX操作共享资源释放锁Lua 脚本先 GET 判断锁是否归属自己再 DEL 释放锁好有了这个完整的锁模型让我们重新回到前面提到的第一个问题。锁过期时间不好评估怎么办锁过期时间不好评估怎么办前面我们提到锁的过期时间如果评估不好这个锁就会有「提前」过期的风险。当时给的妥协方案是尽量「冗余」过期时间降低锁提前过期的概率。这个方案其实也不能完美解决问题那怎么办呢是否可以设计这样的方案加锁时先设置一个过期时间然后我们开启一个「守护线程」定时去检测这个锁的失效时间如果锁快要过期了操作共享资源还未完成那么就自动对锁进行「续期」重新设置过期时间。这确实一种比较好的方案。如果你是 Java 技术栈幸运的是已经有一个库把这些工作都封装好了Redisson。Redisson 是一个 Java 语言实现的 Redis SDK 客户端在使用分布式锁时它就采用了「自动续期」的方案来避免锁过期这个守护线程我们一般也把它叫做「看门狗」线程。除此之外这个 SDK 还封装了很多易用的功能可重入锁乐观锁公平锁读写锁Redlock红锁下面会详细讲这个 SDK 提供的 API 非常友好它可以像操作本地锁的方式操作分布式锁。如果你是 Java 技术栈可以直接把它用起来。这里不重点介绍 Redisson 的使用大家可以看官方 Github 学习如何使用比较简单。到这里我们再小结一下基于 Redis 的实现分布式锁前面遇到的问题以及对应的解决方案死锁设置过期时间过期时间评估不好锁提前过期守护线程自动续期锁被别人释放锁写入唯一标识释放锁先检查标识再释放还有哪些问题场景会危害 Redis 锁的安全性呢之前分析的场景都是锁在「单个」Redis 实例中可能产生的问题并没有涉及到 Redis 的部署架构细节。而我们在使用 Redis 时一般会采用主从集群 哨兵的模式部署这样做的好处在于当主库异常宕机时哨兵可以实现「故障自动切换」把从库提升为主库继续提供服务以此保证可用性。那当「主从发生切换」时这个分布锁会依旧安全吗试想这样的场景客户端 1 在主库上执行 SET 命令加锁成功此时主库异常宕机SET 命令还未同步到从库上主从复制是异步的从库被哨兵提升为新主库这个锁在新的主库上丢失了可见当引入 Redis 副本后分布锁还是可能会受到影响。怎么解决这个问题为此Redis 的作者提出一种解决方案就是我们经常听到的 Redlock红锁。它真的可以解决上面这个问题吗Redlock 真的安全吗好终于到了这篇文章的重头戏。啊上面讲的那么多问题难道只是基础是的那些只是开胃菜真正的硬菜从这里刚刚开始。如果上面讲的内容你还没有理解我建议你重新阅读一遍先理清整个加锁、解锁的基本流程。如果你已经对 Redlock 有所了解这里可以跟着我再复习一遍如果你不了解 Redlock没关系我会带你重新认识它。值得提醒你的是后面我不仅仅是讲 Redlock 的原理还会引出有关「分布式系统」中的很多问题你最好跟紧我的思路在脑中一起分析问题的答案。现在我们来看Redis 作者提出的 Redlock 方案是如何解决主从切换后锁失效问题的。Redlock 的方案基于 2 个前提不再需要部署从库和哨兵实例只部署主库但主库要部署多个官方推荐至少 5 个实例也就是说想用使用 Redlock你至少要部署 5 个 Redis 实例而且都是主库它们之间没有任何关系都是一个个孤立的实例。注意不是部署 Redis Cluster就是部署 5 个简单的 Redis 实例。Redlock 具体如何使用呢整体的流程是这样的一共分为 5 步客户端先获取「当前时间戳T1」客户端依次向这 5 个 Redis 实例发起加锁请求用前面讲到的 SET 命令且每个请求会设置超时时间毫秒级要远小于锁的有效时间如果某一个实例加锁失败包括网络超时、锁被其它人持有等各种异常情况就立即向下一个 Redis 实例申请加锁如果客户端从 3 个大多数以上 Redis 实例加锁成功则再次获取「当前时间戳T2」如果 T2 - T1 锁的过期时间此时认为客户端加锁成功否则认为加锁失败加锁成功去操作共享资源例如修改 MySQL 某一行或发起一个 API 请求加锁失败向「全部节点」发起释放锁请求前面讲到的 Lua 脚本释放锁我简单帮你总结一下有 4 个重点客户端在多个 Redis 实例上申请加锁必须保证大多数节点加锁成功大多数节点加锁的总耗时要小于锁设置的过期时间释放锁要向全部节点发起释放锁请求第一次看可能不太容易理解建议你把上面的文字多看几遍加深记忆。然后记住这 5 步非常重要下面会根据这个流程剖析各种可能导致锁失效的问题假设。好明白了 Redlock 的流程我们来看 Redlock 为什么要这么做。1) 为什么要在多个实例上加锁本质上是为了「容错」部分实例异常宕机剩余的实例加锁成功整个锁服务依旧可用。2) 为什么大多数加锁成功才算成功多个 Redis 实例一起来用其实就组成了一个「分布式系统」。在分布式系统中总会出现「异常节点」所以在谈论分布式系统问题时需要考虑异常节点达到多少个也依旧不会影响整个系统的「正确性」。这是一个分布式系统「容错」问题这个问题的结论是如果只存在「故障」节点只要大多数节点正常那么整个系统依旧是可以提供正确服务的。这个问题的模型就是我们经常听到的「拜占庭将军」问题感兴趣可以去看算法的推演过程。3) 为什么步骤 3 加锁成功后还要计算加锁的累计耗时因为操作的是多个节点所以耗时肯定会比操作单个实例耗时更久而且因为是网络请求网络情况是复杂的有可能存在延迟、丢包、超时等情况发生网络请求越多异常发生的概率就越大。所以即使大多数节点加锁成功但如果加锁的累计耗时已经「超过」了锁的过期时间那此时有些实例上的锁可能已经失效了这个锁就没有意义了。4) 为什么释放锁要操作所有节点在某一个 Redis 节点加锁时可能因为「网络原因」导致加锁失败。例如客户端在一个 Redis 实例上加锁成功但在读取响应结果时网络问题导致读取失败那这把锁其实已经在 Redis 上加锁成功了。所以释放锁时不管之前有没有加锁成功需要释放「所有节点」的锁以保证清理节点上「残留」的锁。好了明白了 Redlock 的流程和相关问题看似 Redlock 确实解决了 Redis 节点异常宕机锁失效的问题保证了锁的「安全性」。但事实真的如此吗Redlock 的争论谁对谁错Redis 作者把这个方案一经提出就马上受到业界著名的分布式系统专家的质疑这个专家叫 Martin是英国剑桥大学的一名分布式系统研究员。在此之前他曾是软件工程师和企业家从事大规模数据基础设施相关的工作。它还经常在大会做演讲写博客写书也是开源贡献者。他马上写了篇文章质疑这个 Redlock 的算法模型是有问题的并对分布式锁的设计提出了自己的看法。之后Redis 作者 Antirez 面对质疑不甘示弱也写了一篇文章反驳了对方的观点并详细剖析了 Redlock 算法模型的更多设计细节。而且关于这个问题的争论在当时互联网上也引起了非常激烈的讨论。二人思路清晰论据充分这是一场高手过招也是分布式系统领域非常好的一次思想的碰撞双方都是分布式系统领域的专家却对同一个问题提出很多相反的论断究竟是怎么回事下面我会从他们的争论文章中提取重要的观点整理呈现给你。提醒后面的信息量极大可能不宜理解最好放慢速度阅读。分布式专家 Martin 对于 Relock 的质疑在他的文章中主要阐述了 4 个论点1) 分布式锁的目的是什么Martin 表示你必须先清楚你在使用分布式锁的目的是什么他认为有两个目的。第一效率。使用分布式锁的互斥能力是避免不必要地做同样的两次工作例如一些昂贵的计算任务。如果锁失效并不会带来「恶性」的后果例如发了 2 次邮件等无伤大雅。第二正确性。使用锁用来防止并发进程互相干扰。如果锁失效会造成多个进程同时操作同一条数据产生的后果是数据严重错误、永久性不一致、数据丢失等恶性问题就像给患者服用了重复剂量的药物后果很严重。他认为如果你是为了前者——效率那么使用单机版 Redis 就可以了即使偶尔发生锁失效宕机、主从切换都不会产生严重的后果。而使用 Redlock 太重了没必要。而如果是为了正确性Martin 认为 Redlock 根本达不到安全性的要求也依旧存在锁失效的问题2) 锁在分布式系统中会遇到的问题Martin 表示一个分布式系统更像一个复杂的「野兽」存在着你想不到的各种异常情况。这些异常场景主要包括三大块这也是分布式系统会遇到的三座大山NPC。NNetwork Delay网络延迟PProcess Pause进程暂停GCCClock Drift时钟漂移Martin 用一个进程暂停GC的例子指出了 Redlock 安全性问题客户端 1 请求锁定节点 A、B、C、D、E客户端 1 的拿到锁后进入 GC时间比较久所有 Redis 节点上的锁都过期了客户端 2 获取到了 A、B、C、D、E 上的锁客户端 1 GC 结束认为成功获取锁客户端 2 也认为获取到了锁发生「冲突」Martin 认为GC 可能发生在程序的任意时刻而且执行时间是不可控的。注当然即使是使用没有 GC 的编程语言在发生网络延迟、时钟漂移时也都有可能导致 Redlock 出现问题这里 Martin 只是拿 GC 举例。3) 假设时钟正确的是不合理的又或者当多个 Redis 节点「时钟」发生问题时也会导致 Redlock 锁失效。客户端 1 获取节点 A、B、C 上的锁但由于网络问题无法访问 D 和 E节点 C 上的时钟「向前跳跃」导致锁到期客户端 2 获取节点 C、D、E 上的锁由于网络问题无法访问 A 和 B客户端 1 和 2 现在都相信它们持有了锁冲突Martin 觉得Redlock 必须「强依赖」多个节点的时钟是保持同步的一旦有节点时钟发生错误那这个算法模型就失效了。即使 C 不是时钟跳跃而是「崩溃后立即重启」也会发生类似的问题。Martin 继续阐述机器的时钟发生错误是很有可能发生的系统管理员「手动修改」了机器时钟机器时钟在同步 NTP 时间时发生了大的「跳跃」总之Martin 认为Redlock 的算法是建立在「同步模型」基础上的有大量资料研究表明同步模型的假设在分布式系统中是有问题的。在混乱的分布式系统的中你不能假设系统时钟就是对的所以你必须非常小心你的假设。4) 提出 fecing token 的方案保证正确性相对应的Martin 提出一种被叫作 fecing token 的方案保证分布式锁的正确性。这个模型流程如下客户端在获取锁时锁服务可以提供一个「递增」的 token客户端拿着这个 token 去操作共享资源共享资源可以根据 token 拒绝「后来者」的请求这样一来无论 NPC 哪种异常情况发生都可以保证分布式锁的安全性因为它是建立在「异步模型」上的。而 Redlock 无法提供类似 fecing token 的方案所以它无法保证安全性。他还表示一个好的分布式锁无论 NPC 怎么发生可以不在规定时间内给出结果但并不会给出一个错误的结果。也就是只会影响到锁的「性能」或称之为活性而不会影响它的「正确性」。Martin 的结论1、Redlock 不伦不类它对于效率来讲Redlock 比较重没必要这么做而对于正确性来说Redlock 是不够安全的。2、时钟假设不合理该算法对系统时钟做出了危险的假设假设多个节点机器时钟都是一致的如果不满足这些假设锁就会失效。3、无法保证正确性Redlock 不能提供类似 fencing token 的方案所以解决不了正确性的问题。为了正确性请使用有「共识系统」的软件例如 Zookeeper。好了以上就是 Martin 反对使用 Redlock 的观点看起来有理有据。下面我们来看 Redis 作者 Antirez 是如何反驳的。Redis 作者 Antirez 的反驳在 Redis 作者的文章中重点有 3 个1) 解释时钟问题首先Redis 作者一眼就看穿了对方提出的最为核心的问题时钟问题。Redis 作者表示Redlock 并不需要完全一致的时钟只需要大体一致就可以了允许有「误差」。例如要计时 5s但实际可能记了 4.5s之后又记了 5.5s有一定误差但只要不超过「误差范围」锁失效时间即可这种对于时钟的精度要求并不是很高而且这也符合现实环境。对于对方提到的「时钟修改」问题Redis 作者反驳到手动修改时钟不要这么做就好了否则你直接修改 Raft 日志那 Raft 也会无法工作...时钟跳跃通过「恰当的运维」保证机器时钟不会大幅度跳跃每次通过微小的调整来完成实际上这是可以做到的为什么 Redis 作者优先解释时钟问题因为在后面的反驳过程中需要依赖这个基础做进一步解释。2) 解释网络延迟、GC 问题之后Redis 作者对于对方提出的网络延迟、进程 GC 可能导致 Redlock 失效的问题也做了反驳我们重新回顾一下Martin 提出的问题假设客户端 1 请求锁定节点 A、B、C、D、E客户端 1 的拿到锁后进入 GC所有 Redis 节点上的锁都过期了客户端 2 获取节点 A、B、C、D、E 上的锁客户端 1 GC 结束认为成功获取锁客户端 2 也认为获取到锁发生「冲突」Redis 作者反驳到这个假设其实是有问题的Redlock 是可以保证锁安全的。这是怎么回事呢还记得前面介绍 Redlock 流程的那 5 步吗这里我再拿过来让你复习一下。客户端先获取「当前时间戳T1」客户端依次向这 5 个 Redis 实例发起加锁请求用前面讲到的 SET 命令且每个请求会设置超时时间毫秒级要远小于锁的有效时间如果某一个实例加锁失败包括网络超时、锁被其它人持有等各种异常情况就立即向下一个 Redis 实例申请加锁如果客户端从 3 个大多数以上 Redis 实例加锁成功则再次获取「当前时间戳T2」如果 T2 - T1 锁的过期时间此时认为客户端加锁成功否则认为加锁失败加锁成功去操作共享资源例如修改 MySQL 某一行或发起一个 API 请求加锁失败向「全部节点」发起释放锁请求前面讲到的 Lua 脚本释放锁注意重点是 1-3在步骤 3加锁成功后为什么要重新获取「当前时间戳T2」还用 T2 - T1 的时间与锁的过期时间做比较Redis 作者强调如果在 1-3 发生了网络延迟、进程 GC 等耗时长的异常情况那在第 3 步 T2 - T1是可以检测出来的如果超出了锁设置的过期时间那这时就认为加锁会失败之后释放所有节点的锁就好了Redis 作者继续论述如果对方认为发生网络延迟、进程 GC 是在步骤 3 之后也就是客户端确认拿到了锁去操作共享资源的途中发生了问题导致锁失效那这不止是 Redlock 的问题任何其它锁服务例如 Zookeeper都有类似的问题这不在讨论范畴内。这里我举个例子解释一下这个问题客户端通过 Redlock 成功获取到锁通过了大多数节点加锁成功、加锁耗时检查逻辑客户端开始操作共享资源此时发生网络延迟、进程 GC 等耗时很长的情况此时锁过期自动释放客户端开始操作 MySQL此时的锁可能会被别人拿到锁失效Redis 作者这里的结论就是客户端在拿到锁之前无论经历什么耗时长问题Redlock 都能够在第 3 步检测出来客户端在拿到锁之后发生 NPC那 Redlock、Zookeeper 都无能为力所以Redis 作者认为 Redlock 在保证时钟正确的基础上是可以保证正确性的。3) 质疑 fencing token 机制Redis 作者对于对方提出的 fecing token 机制也提出了质疑主要分为 2 个问题这里最不宜理解请跟紧我的思路。第一这个方案必须要求要操作的「共享资源服务器」有拒绝「旧 token」的能力。例如要操作 MySQL从锁服务拿到一个递增数字的 token然后客户端要带着这个 token 去改 MySQL 的某一行这就需要利用 MySQL 的「事物隔离性」来做。// 两个客户端必须利用事物和隔离性达到目的 // 注意 token 的判断条件 UPDATE table T SET val  $new_val WHERE id  $id AND current_token  $token但如果操作的不是 MySQL 呢例如向磁盘上写一个文件或发起一个 HTTP 请求那这个方案就无能为力了这对要操作的资源服务器提出了更高的要求。也就是说大部分要操作的资源服务器都是没有这种互斥能力的。再者既然资源服务器都有了「互斥」能力那还要分布式锁干什么所以Redis 作者认为这个方案是站不住脚的。第二退一步讲即使 Redlock 没有提供 fecing token 的能力但 Redlock 已经提供了随机值就是前面讲的 UUID利用这个随机值也可以达到与 fecing token 同样的效果。如何做呢Redis 作者只是提到了可以完成 fecing token 类似的功能但却没有展开相关细节根据我查阅的资料大概流程应该如下如有错误欢迎交流~客户端使用 Redlock 拿到锁客户端在操作共享资源之前先把这个锁的 VALUE在要操作的共享资源上做标记客户端处理业务逻辑最后在修改共享资源时判断这个标记是否与之前一样一样才修改类似 CAS 的思路还是以 MySQL 为例举个例子就是这样的客户端使用 Redlock 拿到锁客户端要修改 MySQL 表中的某一行数据之前先把锁的 VALUE 更新到这一行的某个字段中这里假设为 current_token 字段)客户端处理业务逻辑客户端修改 MySQL 的这一行数据把 VALUE 当做 WHERE 条件再修改UPDATE table T SET val  $new_val WHERE id  $id AND current_token  $redlock_value可见这种方案依赖 MySQL 的事物机制也达到对方提到的 fecing token 一样的效果。但这里还有个小问题是网友参与问题讨论时提出的两个客户端通过这种方案先「标记」再「检查修改」共享资源那这两个客户端的操作顺序无法保证啊而用 Martin 提到的 fecing token因为这个 token 是单调递增的数字资源服务器可以拒绝小的 token 请求保证了操作的「顺序性」Redis 作者对这问题做了不同的解释我觉得很有道理他解释道分布式锁的本质是为了「互斥」只要能保证两个客户端在并发时一个成功一个失败就好了不需要关心「顺序性」。前面 Martin 的质疑中一直很关心这个顺序性问题但 Redis 的作者的看法却不同。综上Redis 作者的结论1、作者同意对方关于「时钟跳跃」对 Redlock 的影响但认为时钟跳跃是可以避免的取决于基础设施和运维。2、Redlock 在设计时充分考虑了 NPC 问题在 Redlock 步骤 3 之前出现 NPC可以保证锁的正确性但在步骤 3 之后发生 NPC不止是 Redlock 有问题其它分布式锁服务同样也有问题所以不在讨论范畴内。是不是觉得很有意思在分布式系统中一个小小的锁居然可能会遇到这么多问题场景影响它的安全性不知道你看完双方的观点更赞同哪一方的说法呢别急后面我还会综合以上论点谈谈自己的理解。好讲完了双方对于 Redis 分布锁的争论你可能也注意到了Martin 在他的文章中推荐使用 Zookeeper 实现分布式锁认为它更安全确实如此吗基于 Zookeeper 的锁安全吗如果你有了解过 Zookeeper基于它实现的分布式锁是这样的客户端 1 和 2 都尝试创建「临时节点」例如 /lock假设客户端 1 先到达则加锁成功客户端 2 加锁失败客户端 1 操作共享资源客户端 1 删除 /lock 节点释放锁你应该也看到了Zookeeper 不像 Redis 那样需要考虑锁的过期时间问题它是采用了「临时节点」保证客户端 1 拿到锁后只要连接不断就可以一直持有锁。而且如果客户端 1 异常崩溃了那么这个临时节点会自动删除保证了锁一定会被释放。不错没有锁过期的烦恼还能在异常时自动释放锁是不是觉得很完美其实不然。思考一下客户端 1 创建临时节点后Zookeeper 是如何保证让这个客户端一直持有锁呢原因就在于客户端 1 此时会与 Zookeeper 服务器维护一个 Session这个 Session 会依赖客户端「定时心跳」来维持连接。如果 Zookeeper 长时间收不到客户端的心跳就认为这个 Session 过期了也会把这个临时节点删除。同样地基于此问题我们也讨论一下 GC 问题对 Zookeeper 的锁有何影响客户端 1 创建临时节点 /lock 成功拿到了锁客户端 1 发生长时间 GC客户端 1 无法给 Zookeeper 发送心跳Zookeeper 把临时节点「删除」客户端 2 创建临时节点 /lock 成功拿到了锁客户端 1 GC 结束它仍然认为自己持有锁冲突可见即使是使用 Zookeeper也无法保证进程 GC、网络延迟异常场景下的安全性。这就是前面 Redis 作者在反驳的文章中提到的如果客户端已经拿到了锁但客户端与锁服务器发生「失联」例如 GC那不止 Redlock 有问题其它锁服务都有类似的问题Zookeeper 也是一样所以这里我们就能得出结论了一个分布式锁在极端情况下不一定是安全的。如果你的业务数据非常敏感在使用分布式锁时一定要注意这个问题不能假设分布式锁 100% 安全。好现在我们来总结一下 Zookeeper 在使用分布式锁时优劣Zookeeper 的优点不需要考虑锁的过期时间watch 机制加锁失败可以 watch 等待锁释放实现乐观锁但它的劣势是性能不如 Redis部署和运维成本高客户端与 Zookeeper 的长时间失联锁被释放问题我对分布式锁的理解好了前面详细介绍了基于 Redis 的 Redlock 和 Zookeeper 实现的分布锁在各种异常情况下的安全性问题下面我想和你聊一聊我的看法仅供参考不喜勿喷。1) 到底要不要用 Redlock前面也分析了Redlock 只有建立在「时钟正确」的前提下才能正常工作如果你可以保证这个前提那么可以拿来使用。但保证时钟正确我认为并不是你想的那么简单就能做到的。第一从硬件角度来说时钟发生偏移是时有发生无法避免。例如CPU 温度、机器负载、芯片材料都是有可能导致时钟发生偏移的。第二从我的工作经历来说曾经就遇到过时钟错误、运维暴力修改时钟的情况发生进而影响了系统的正确性所以人为错误也是很难完全避免的。所以我对 Redlock 的个人看法是尽量不用它而且它的性能不如单机版 Redis部署成本也高我还是会优先考虑使用主从 哨兵的模式 实现分布式锁。那正确性如何保证呢第二点给你答案。2) 如何正确使用分布式锁在分析 Martin 观点时它提到了 fecing token 的方案给我了很大的启发虽然这种方案有很大的局限性但对于保证「正确性」的场景是一个非常好的思路。所以我们可以把这两者结合起来用1、使用分布式锁在上层完成「互斥」目的虽然极端情况下锁会失效但它可以最大程度把并发请求阻挡在最上层减轻操作资源层的压力。2、但对于要求数据绝对正确的业务在资源层一定要做好「兜底」设计思路可以借鉴 fecing token 的方案来做。两种思路结合我认为对于大多数业务场景已经可以满足要求了。总结好了总结一下。这篇文章我们主要探讨了基于 Redis 实现的分布式锁究竟是否安全这个问题。从最简单分布式锁的实现到处理各种异常场景再到引出 Redlock以及两个分布式专家的辩论得出了 Redlock 的适用场景。最后我们还对比了 Zookeeper 在做分布式锁时可能会遇到的问题以及与 Redis 的差异。这里我把这些内容总结成了思维导图方便你理解。后记这篇文章的信息量其实是非常大的应该把分布锁的问题彻底讲清楚了。如果你没有理解建议你多读几遍并在脑海中构建各种假定的场景反复思辨。在写这篇文章时我又重新研读了两位大神关于 Redlock 争辩的这两篇文章可谓是是收获满满在这里也分享一些心得给你。1、在分布式系统环境下看似完美的设计方案可能并不是那么「严丝合缝」如果稍加推敲就会发现各种问题。所以在思考分布式系统问题时一定要谨慎再谨慎。2、从 Redlock 的争辩中我们不要过多关注对错而是要多学习大神的思考方式以及对一个问题严格审查的严谨精神。最后用 Martin 在对于 Redlock 争论过后写下的感悟来结尾“前人已经为我们创造出了许多伟大的成果站在巨人的肩膀上我们可以才得以构建更好的软件。无论如何通过争论和检查它们是否经得起别人的详细审查这是学习过程的一部分。但目标应该是获取知识而不是为了说服别人让别人相信你是对的。有时候那只是意味着停下来好好地想一想。”共勉。我是 Kaito是一个对于技术有思考的资深后端程序员在我的文章中我不仅会告诉你一个技术点是什么还会告诉你为什么这么做我还会尝试把这些思考过程提炼成通用的方法论让你可以应用在其它领域中做到举一反三。往期推荐对象存储为什么那么火高性能开发别点发际线要紧什么是自动驾驶被 AI 算法“监控”的打工人点分享点收藏点点赞点在看
http://www.sadfv.cn/news/298775/

相关文章:

  • 网站速度慢的原因北京网站推广公司排名
  • 淄博网站制作公司深圳企业网站建设服务公司
  • 做服装店网站的素材深圳宝安做网站的公司
  • 推广型网站建设机构网页设计与网站开发的实践目的
  • 荷城网站设计有关大学生做兼职的网站
  • 自己做图片的网站链接上海网站seo
  • 系统网站建设方案郑州网站建设哪里好
  • 保亭整站优化手机互动网站建设
  • 凡科网站建设公司临沂谁会做网站
  • wamp做的网站上传百度企业查询官网
  • wordpress连通公众号seo搜外
  • 鸣蝉建站平台快速排名点击工具
  • 做mip网站必须备案吗o2o网站开发价格
  • 0317网站建设甘肃公司的网络营销方案
  • 哪些网站可以做商家苏州网站制作 网站
  • 阿里巴巴网站上面产品描述一般怎么做的邯郸网站设计建设
  • 到哪个网站做任务写作网站挣钱对比
  • 深圳集团网站开发网站开发公司专门做网站的公司有哪些
  • 深圳专业做网站和seo的公司html做的网页怎么变成网站
  • 江苏江都建设集团有限公司官方网站定远建设局官方网站
  • 怎么把一个网站设置成首页广东建设协会网站
  • 网站空间 云端网站名称和备案的不一样
  • 服装生产厂商网站建设方案手机网站需要什么
  • 十堰建设网站首页wordpress标签管理系统
  • 专业网站设计制作改版电子商务网站建设与管理期末
  • vellum wordpressseo主要做哪些工作
  • 可以绑定域名的免费网站扫码进入网站 怎么做
  • 门户网站特点把网站提交给百度
  • 夜夜做新郎网站在线视频网站建设中的服务器搭建方式
  • 淘宝刷单网站怎么建设源代码付费推广渠道有哪些