天津网站建设方案服务,怎样创建网站平台,学生个人网页制作代码,网站开发与服务合同范本浅谈redis分布式锁
分布式锁介绍
分布式锁#xff0c;顾名思义#xff0c;分布式系统中的锁#xff0c;当多个进程不在同一个系统中时#xff0c;用分布式锁控制各个进程对共享资源的访问#xff0c;通过互斥来保持一致性。
使用场景#xff1a;电商中某商品的秒杀活动…浅谈redis分布式锁
分布式锁介绍
分布式锁顾名思义分布式系统中的锁当多个进程不在同一个系统中时用分布式锁控制各个进程对共享资源的访问通过互斥来保持一致性。
使用场景电商中某商品的秒杀活动接口中的幂等性校验等
分布式锁的特性
1互斥性。锁的目的是获取共享资源的使用权则需保证在并发情况下同一时刻只有一个线程能获得锁 2加锁解锁对称性。线程使用加锁获得对共享资源的使用后使用完毕必须解锁即加锁解锁需为同一个线程 3防止死锁。假如由系统等原因出现宕机情况导致线程获取到锁后来不及解锁而其他线程无法获取到锁此情况为死锁避免此情况有必要设置锁的有效时间确保系统出故障在超出锁的有效时间后其他线程能获取到锁。 4锁粒度尽量小。锁的颗粒度要尽量小避免导致大量线程同时为获取同一个锁而造成阻塞 5可重入性。同一个线程在锁使用期间可以重复拿到同一个资源的锁。
常用的三种分布式锁
1 基于数据库表主键唯一性原理、排他锁实现分布式锁 2 基于ZK临时有序节点实现的分布式锁 3 基于redis中setnx命令的原子性实现的分布式锁
Redis分布式锁的阶段性进展
基于redis为目前最常见的锁及之前介绍的redis原理简单介绍下redis分布式锁
实现一个简易的Redis分布式锁
阶段性1、利用redis的setnx和expire命令设置一个简单的redis锁代码如下 String thread thread;Boolean lock redisTemplate.opsForValue().setIfAbsent(lock, thread,30, TimeUnit.SECONDS);if(lock){//1加锁成功,处理handle(此处执行业务逻辑);//2处理完毕后解锁Object lockValue redisTemplate.opsForValue().get(lock);if(thread.equals(lockValue)){//根据值对比判断是此线程的锁后删除锁redisTemplate.delete(lock);// 删除锁}}else{// 加锁失败: 重试或者直接返回获取锁失败retryOrReturn();}
以上代码看着没什么问题实现了针对某个线程获取锁且设置超时时间并且根据值对比判断只能此线程解锁。然而忽略一个问题由于在获取锁并删除这个过程中并非原子性假如线程A删除锁的时候锁已超时自动解锁同时其他线程B获取到锁此时A把B持有的锁给删除。 那么如何实现锁对比判断和删除是一个原子性呢见阶段性2
引入LUA
阶段性2、引入LUA删除锁 引入LUA在获取锁的value值对比是否一致假如相等则删除此段过程保证其原子性。代码如下 String lockKey lock;String thread thread;Boolean lock redisTemplate.opsForValue().setIfAbsent(lockKey, thread,30, TimeUnit.SECONDS);if(lock){//1加锁成功,处理handle(此处执行业务逻辑);//2处理完毕后解锁Object lockValue redisTemplate.opsForValue().get(lock);//LUA脚本String script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end;// 原子删除Object lock1 redisTemplate.execute(new DefaultRedisScriptInteger(script, Integer.class), Arrays.asList(lockKey, thread));}else{// 加锁失败: 重试或者直接返回获取锁失败retryOrReturn();}框架Redission
以上结合redisLua的实现在框架redission中均一实。Redission在一个提供redis基础功能实现的情况下还提供了一系列的分布式服务。从而使使用者的开发能够集中的专注于业务逻辑上。 除redission具备了锁的锁的互斥性和可重入性等基本功能还增加了其他功能比如引入Watch Dog解决了一个关于锁的自动续期问题以及引入Red Lock增加了一些更安全的锁实现 简易代码如下 RLock lock redisson.getLock (lock);//获取锁或阻塞等待lock.lock ();try {handle(此处执行业务逻辑);} finally {//释放锁已封装获取到此线程的锁并封装lock.unlock ();}看门狗(Watch Dog
场景假如线程A获取到锁后由于执行的逻辑耗时比较长 在运行期间超出了默认的超时时间30s范围。则出现在线程A未执行完毕正常释放锁的情况下由超时解锁线程B重新获取到锁引发故障。 针对此场景引入Redission的Watch Dog实现自动续期保证在线程A使用期间不会超时而引发其他多个线程同时持有锁的情况 原理看门狗相当于是一个定时任务线程一旦加锁成功会对应启动一个看门狗属于后台线程。每10s观察线程是否还持有锁如果有则延迟锁的的持有时间将时间重置到30s直至线程主动解锁或者系统故障看门狗不执行 注意如果指定超时时间不会自动续签时间此时需保证线程执行业务逻辑的时间务必大于指定超时时间。
关于红锁RedLock
场景假设在集群中有多个redis master节点这些节点是完全独立的其中一个master获取到锁后发生故障此时还未来得及发生主从复制即key未来得及同步到从节点上。而此时通过哨兵选举其一slave节点升级为master节点。那么此时会出现后续应用会申请到同一个锁则此时同一个锁被获取了不只一次导致出现问题。 针对以上情况引入红锁利用多个 Redission node 最终 组成 RedLock分布式锁Redission node 是互相独立的不存在任何复制或者其他隐含的分布式协调机制。解决主从结构下存在的安全问题。
RedLock特点 加锁过程中在一个redis集群中依次从N个reidis节点上获取锁需要相同的key和value并且至少半数以上N/21的redis节点获取到锁才算是获取锁成功否则获取失败。红锁以节点组的方式解决单个节点出现故障的情况。 ** 场景**假设redis集群中五个主节点客户端申请获取锁的请求到了redis节点节点三个A\B\C获取到锁并成功执行setnx操作此时假如其中一节点A宕机则返回给客户端的响应失败在客户端层面看是获取锁超时而失败但是在redis集群看来是获取锁成功。然后客户端在释放锁时也会对那些获取锁失败的redis节点发起同样的请求。
RedLock弊端在加锁/解锁多个节点其过程均耗费时间性能较低。针对红锁假如全部redis重启也可能会出现锁失效的问题。 因此是否使用红锁需结合实际场景使用