Redis高并发分布式锁详解( 七 )


zookeeper满足CP , 在Master节点上写成功了会优先同步给从节点【ZAB协议(半数以上写成功)】 , 之后在返回给客户端
【2】主从架构锁失效问题:
redis会出现 , 因为从节点变成主节点时 , 会出现丢失数据的问题 。
zookeeper不会出现 , 因为从节点变成主节点时 , 不会会出现丢失数据的问题 。
【3】集群下性能角度:
redis性能会高于zookeeper , 同步是个耗时的操作(而且这个过程中还是相当于阻塞线程) , 并发越高的情况 , 我们想要的是耗时越少的越好 。
3.选redis还是zk实现分布式锁:
首先zk的性能肯定不如redis , 但是从分布式锁的角度语义上来说 , zk可能更适合一些 , 所以如果对性能要求比较高的话就选redis , 对数据的强一致性有特别严格要求的话就选zk , 现在的主流的分布式锁方案还是redis , 也有一些办法去减少redis主从架构锁失效问题 。
如何提升分布式锁性能问题分析1.分布式锁为我们解决了并发问题 , 但是其底层思路是将并行执行的请求给串行化了 , 因为redis是单线程执行任务的 , 肯定就不会有并发问题了 。
2.但是这种设计本身是与我们高并发的需求是冲突的 。但是某些场景下我们又不得不用 , 所以我们应该基于场景做一些优化 。
3.正如阿里巴巴Java开发手册里面写到:
6. 【强制】高并发时 , 同步调用应该去考量锁的性能损耗 。能用无锁数据结构 , 就不要用锁;能锁区块 , 就不要锁整个方法体;能用对象锁 , 就不要用类锁 。说明:尽可能使加锁的代码块工作量尽可能的小 , 避免在锁代码块中调用 RPC 方法 。7. 【强制】对多个资源、数据库表、对象同时加锁时 , 需要保持一致的加锁顺序 , 否则可能会造成死锁 。说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作 , 那么线程二的加锁顺序也必须是 A、B、C , 否则可能出现死锁 。8. 【强制】并发修改同一记录时 , 避免更新丢失 , 需要加锁 。要么在应用层加锁 , 要么在缓存加锁 , 要么在数据库层使用乐观锁 , 使用 version 作为更新依据 。说明:如果每次访问冲突概率小于 20% , 推荐使用乐观锁 , 否则使用悲观锁 。乐观锁的重试次数不得小于 3 次 。4.所以我们优先从锁的粒度开始 , 锁是否合适 , 加锁的范围是否够小 。锁的粒度范围越小越好 , 加锁的代码越少性能就越高 , 因为加锁的代码会串行执行 , 没有必要加锁的代码肯定是让他们并行执行这样效率更高 。
案例演示场景说明:
在秒杀抢购的情况下 , 大量的秒杀商品其实都是走同一逻辑的 , 如果使用公用的锁必然是不合适的 , 这会大大阻塞住整个系统 , 而且不同商品之前根本不存在竞争关系 , 故一般我们会采用类似 redis_promotion_product_stock_$productId :1000 这种设置库存值。那么对于每个商品既然拥有了自己的库存那么对于对应库存加锁就能缩小了锁的颗粒度 。
但是这种真的就可行了嘛?对A商品的下单 , 都必对"redis_promotion_product_lock_A"这个锁key来加锁 。这样会导致对同一个商品的下单请求 , 就必须串行化 , 一个接一个的处理 。假设加锁之后 , 释放锁之前 , 查库存 -> 创建订单 -> 扣减库存 , 这个过程性能很高吧 , 算他全过程20毫秒 , 这应该不错了 。那么1秒是1000毫秒 , 只能容纳50个对这个商品的请求依次串行完成处理 。这种性能远远不能满足我们想要的 。而且对于变量进行原子操作这种:

经验总结扩展阅读