设计很好的视觉很棒的网站,ofo的网站用什么做的,asp网站后台密码文件,做婚庆网站的功能定位文章目录 Redis分布式锁方案#xff1a;SETNX EXPIRE基本原理比较好的实现会产生四个问题 几种解决原子性的方案方案#xff1a;SETNX value值是#xff08;系统时间过期时间#xff09;方案#xff1a;使用Lua脚本(包含SETNX EXPIRE两条指令)方案#xff1a;SET的扩展… 文章目录 Redis分布式锁方案SETNX EXPIRE基本原理比较好的实现会产生四个问题 几种解决原子性的方案方案SETNX value值是系统时间过期时间方案使用Lua脚本(包含SETNX EXPIRE两条指令)方案SET的扩展命令SET EX PX NX会出现的问题 方案: 开源框架:Redisson方案多机实现的分布式锁Redlock总结参考链接 Redis分布式锁
方案SETNX EXPIRE
基本原理
SETNX 是SET IF NOT EXISTS的简写.日常命令格式是SETNX key value如果 key不存在则SETNX成功返回1如果这个key已经存在了则返回0。 redis语法
使用 SETNX 命令设置键值为锁的标识
键名为 lock_key值为锁标识 lock_value
SETNX 返回 1 表示成功设置0 表示键已存在
SETNX lock_key lock_value
使用 EXPIRE 命令设置键的过期时间例如设置为 10 秒
EXPIRE lock_key 10用java来实现 ValueOperationsString, String valueOps redisTemplate.opsForValue();// 使用 setIfAbsent 方法设置键值对仅当键不存在时才进行设置boolean isSet valueOps.setIfAbsent(key, value);if (isSet) {// 如果成功设置再使用 expire 方法设置过期时间redisTemplate.expire(key, ttlInSeconds, java.util.concurrent.TimeUnit.SECONDS);System.out.println(Key set successfully with TTL!);} else {System.out.println(Key already exists, not set.);}比较好的实现 private static final String LOCK_SUCCESS OK;private static final String SET_IF_NOT_EXIST NX;private static final String SET_WITH_EXPIRE_TIME PX;/*** 尝试获取分布式锁* param jedis Redis客户端* param lockKey 锁* param requestId 请求标识* param expireTime 超期时间* return 是否获取成功*/public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {String result jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if (LOCK_SUCCESS.equals(result)) {return true;}return false;}第一个为key我们使用key来当锁因为key是唯一的。 第二个为value我们传的是requestId很多童鞋可能不明白有key作为锁不就够了吗为什么还要用到value原因就是我们在上面讲到可靠性时分布式锁要满足第四个条件解铃还须系铃人通过给value赋值为requestId我们就知道这把锁是哪个请求加的了在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。 第三个为nxxx这个参数我们填的是NX意思是SET IF NOT EXIST即当key不存在时我们进行set操作若key已经存在则不做任何操作 第四个为expx这个参数我们传的是PX意思是我们要给这个key加一个过期的设置具体时间由第五个参数决定。 第五个为time与第四个参数相呼应代表key的过期时间。
会产生四个问题
1、setnx和expire两个命令分开了不是原子操作。如果执行完setnx加锁正要执行expire设置过期时间时进程crash或者要重启维护了那么这个锁就“长生不老”了别的线程永远获取不到锁啦。 2、超时解锁会导致并发问题如果两个线程同时要获取锁此时所恰好过期此时这两个线程能够同时进入产生并发问题 3、不可重入同一个线程想要同时获得这个锁两次实际是是不支持的 4、无法等待锁释放在没有获取锁的时候会直接返回没有等待时间。
其中原子性问题是一个最重要的问题因此有了下面的解决方案。
几种解决原子性的方案
方案SETNX value值是系统时间过期时间
实际上是一种逻辑过期时间将过期的时间作为value用setnx的value值里如果加锁失败则拿出value校验一下即可。 java实现
long expires System.currentTimeMillis() expireTime; //系统时间设置的过期时间
String expiresStr String.valueOf(expires);// 如果当前锁不存在返回加锁成功
if (jedis.setnx(key_resource_id, expiresStr) 1) {return true;
}
// 如果锁已经存在获取锁的过期时间
String currentValueStr jedis.get(key_resource_id);// 如果获取到的过期时间小于系统当前时间表示已经过期
if (currentValueStr ! null Long.parseLong(currentValueStr) System.currentTimeMillis()) {// 锁已过期获取上一个锁的过期时间并设置现在锁的过期时间不了解redis的getSet命令的小伙伴可以去官网看下哈String oldValueStr jedis.getSet(key_resource_id, expiresStr);if (oldValueStr ! null oldValueStr.equals(currentValueStr)) {// 考虑多线程并发的情况只有一个线程的设置值和当前值相同它才可以加锁return true;}
}
//其他情况均返回加锁失败
return false;
}方案使用Lua脚本(包含SETNX EXPIRE两条指令)
实现原理调用lua代码一个lua脚本是原子的。
if redis.call(setnx,KEYS[1],ARGV[1]) 1 thenredis.call(expire,KEYS[1],ARGV[2])
elsereturn 0
end;java代码 String lua_scripts if redis.call(setnx,KEYS[1],ARGV[1]) 1 then redis.call(expire,KEYS[1],ARGV[2]) return 1 else return 0 end;
Object result jedis.eval(lua_scripts, Collections.singletonList(key_resource_id), Collections.singletonList(values));
//判断是否成功
return result.equals(1L);方案SET的扩展命令SET EX PX NX
保证SETNX EXPIRE两条指令的原子性我们还可以巧用Redis的SET指令扩展参数 例如SET key value[EX seconds][PX milliseconds][NX|XX]它也是原子性的 语法如下
SET key value[EX seconds][PX milliseconds][NX|XX]NX :表示key不存在的时候才能set成功也即保证只有第一个客户端请求才能获得锁而其他客户端请求只能等其释放锁才能获取。
EX seconds :设定key的过期时间时间单位是秒。
PX milliseconds: 设定key的过期时间单位为毫秒
XX: 仅当key存在时设置值java代码
ifjedis.set(key_resource_id, lock_value, NX, EX, 100s) 1{ //加锁try {do something //业务处理}catch(){}finally {jedis.del(key_resource_id); //释放锁}
}会出现的问题
这种方案能解决方案一的原子性问题但是依然会存在很大的问题如下所示 1、时钟不同步如果不同的节点的系统时钟不同步可能导致锁的过期时间计算不准确。 解决方案使用相对时间而非绝对时间或者使用时钟同步工具确保系统时钟同步。 2、死锁在某些情况下可能出现死锁例如由于网络问题导致锁的释放操作未能执行。 解决方案使用带有超时和重试的锁获取和释放机制确保在一定时间内能够正常操作。 3、锁过期与业务未完成如果业务逻辑执行时间超过了设置的过期时间锁可能在业务未完成时自动过期导致其他客户端获取到锁。 解决方案可以设置更长的过期时间确保业务有足够的时间完成。或者在业务未完成时通过更新锁的过期时间来延长锁的生命周期。 4、锁的争用多个客户端同时尝试获取锁可能导致锁的频繁争用。 解决方案可以使用带有重试机制的获取锁操作或者采用更复杂的锁实现如 Redlock 算法。 5、锁的释放问题客户端获取锁后发生异常或未能正常释放锁可能导致其他客户端无法获取锁。 解决方案使用 SET 命令设置锁的值并在释放锁时检查当前值是否匹配。只有匹配时才执行释放锁的操作。 6、锁被别的线程误删假设线程a执行完后去释放锁。但是它不知道当前的锁可能是线程b持有的线程a去释放锁时有可能过期时间已经到了此时线程b进来占有了锁。那线程a就把线程b的锁释放掉了但是线程b临界区业务代码可能都还没执行完。 解决方案SET EX PX NX 校验唯一随机值给value值设置一个标记当前线程唯一的随机数在删除的时候校验一下需要用乱
方案: 开源框架:Redisson
总结一下上面的解决问题的历程和问题用SETNXEXPIRE可以解决分布式锁的问题但是这种方式不是原子性操作。因此在提出的三种原子性操作解决方法但是依然会出现几个问题在会出现的问题中简单罗列了几种问题与解决方法其中问题3中有锁过期与业务未完成有一个系统的解决方案即接下来介绍的Redison。 Redisson 是一个基于 Redis 的 Java 驱动库提供了分布式、高性能的 Java 对象操作服务这里只探讨分布式锁的原理
只要线程一加锁成功就会启动一个watch dog看门狗它是一个后台线程会每隔10秒检查一下如果线程1还持有锁那么就会不断的延长锁key的生存时间。因此Redisson就是使用Redisson解决了锁过期释放业务没执行完问题。
Watchdog 定期续期锁 当客户端成功获取锁后Redisson 启动一个 Watchdog 线程该线程会定期通常是锁过期时间的一半检查锁是否过期并在过期前对锁进行续期。 Watchdog 使用 Lua 脚本确保原子性 为了确保 Watchdog 操作的原子性Redisson 使用 Lua 脚本执行 Watchdog 操作。这样在 Watchdog 检查和续期锁的过程中可以保证整个操作是原子的防止出现竞争条件。 Watchdog 续期锁的过期时间 Watchdog 线程会通过使用 PEXPIRE 或者 EXPIRE 命令来续期锁的过期时间。这样在业务未完成时锁的过期时间会不断延长直到业务完成释放锁。
方案多机实现的分布式锁Redlock
Redisson分布式锁会有个缺陷就是在Redis哨兵模式下:客户端1 对某个 master节点 写入了redisson锁此时会异步复制给对应的 slave节点。但是这个过程中一旦发生master节点宕机主备切换slave节点从变为了 master节点。 这时 客户端2 来尝试加锁的时候在新的master节点上也能加锁此时就会导致多个客户端对同一个分布式锁完成了加锁。 这时系统在业务语义上一定会出现问题 导致各种脏数据的产生 。 因此有了Redlock Redlock全名叫做 Redis Distributed Lock;即使用redis实现的分布式锁 使用场景多个服务间保证同一时刻同一时间段内同一用户只能有一个请求防止关键业务出现并发攻击 基本原理 按顺序向5个master节点请求加锁注意这五个节点不是哨兵和master-slaver 根据设置的超时时间来判断是不是要跳过该master节点。 如果大于等于三个节点加锁成功并且使用的时间小于锁的有效期即可认定加锁成功啦。 如果获取锁失败解锁 1、时钟漂移问题 问题描述 不同的服务器上的系统时钟可能存在一定的漂移导致在不同节点上计算锁的过期时间不一致。 解决方案 使用 NTP 等工具同步服务器时钟确保各个节点时钟同步。此外可以选择使用更精确的锁实现如 Redisson 的 Redlock 的改进版本可以在获取锁时计算一个时钟偏移量使得各个节点的时钟更一致。 2. 网络分区和节点故障 问题描述 网络分区或节点故障可能导致锁的不一致状态。 解决方案 针对网络分区可以采用心跳机制定期检测节点的健康状态。对于节点故障可以使用更多的节点超过一半以确保容错性。另外可以考虑使用 Redis Sentinel 或 Redis Cluster 等 Redis 提供的高可用性方案以降低节点故障的影响。 3. 性能代价问题 问题描述 Redlock 的性能代价相对较高因为需要在多个节点上执行锁的获取和释放操作。 解决方案 对于一些对性能要求较高的场景可以考虑使用更轻量级的锁算法例如基于单一节点的分布式锁实现。在一些场景下性能可能更为重要。 4. 容错性和安全性 问题描述 由于 Redis 是基于内存的数据库节点故障可能导致数据丢失。另外需要确保所有的 Redis 节点都是可信任的。 解决方案 在容错性方面可以通过配置 Redis Sentinel 或 Redis Cluster 来提高 Redis 的高可用性。在安全性方面需要确保 Redis 部署在受信任的网络中并采取相应的网络安全措施例如使用密码保护 Redis。
因此可以看出实际上这种方案也有很大的问题需要谨慎的去使用总之系统服务不能假定所有的客户端都表现的符合预期。从安全角度讲服务端必须防范这种来自客户端的滥用。
总结
在单体redis中通过SETNX EXPIRE方式可以为多个JVM加一个分布式锁但是由于操作的非原子性会导致并发问题因此出现了几种原子性解决方法包括SETNX时间value、lua脚本和SET扩展命令的方式解决但是依然会出现事务还没完成时间就失效产生了新一轮并发因此通过添加一个看门狗线程定期检查能够解决这个问题对于一个Java开发来说有一个Redisson框架实际上封装了lua脚本来实现。 哨兵和主从模式下的分布式redis如果一个主机更新了锁但是恰好此时master发生了意外还没有同步到slaver此时新出现的master没有加锁依然会产生问题。由此出现了RedlockRerdisson的解决方案但是多个redis的集群会带来一些性能的损耗而且由于时钟不同步意外情况的发生也不能保证这种方案是一定的。
参考链接
https://mp.weixin.qq.com/s/8fdBKAyHZrfHmSajXT_dnA https://xiaomi-info.github.io/2019/12/17/redis-distributed-lock/ https://juejin.cn/post/6936956908007850014#heading-2 https://zhuanlan.zhihu.com/p/440865954 https://mp.weixin.qq.com/s?__bizMzg3NjU3NTkwMQmid2247505097idx1sn5c03cb769c4458350f4d4a321ad51f5asource41#wechat_redirect 文章转载自: http://www.morning.nrll.cn.gov.cn.nrll.cn http://www.morning.bwxph.cn.gov.cn.bwxph.cn http://www.morning.mfsxd.cn.gov.cn.mfsxd.cn http://www.morning.mlnzx.cn.gov.cn.mlnzx.cn http://www.morning.qqbw.cn.gov.cn.qqbw.cn http://www.morning.bxgpy.cn.gov.cn.bxgpy.cn http://www.morning.kxscs.cn.gov.cn.kxscs.cn http://www.morning.rqqkc.cn.gov.cn.rqqkc.cn http://www.morning.lhygbh.com.gov.cn.lhygbh.com http://www.morning.yesidu.com.gov.cn.yesidu.com http://www.morning.qftzk.cn.gov.cn.qftzk.cn http://www.morning.beiyishengxin.cn.gov.cn.beiyishengxin.cn http://www.morning.ktskc.cn.gov.cn.ktskc.cn http://www.morning.rxcqt.cn.gov.cn.rxcqt.cn http://www.morning.ljtwp.cn.gov.cn.ljtwp.cn http://www.morning.cwznh.cn.gov.cn.cwznh.cn http://www.morning.hlfgm.cn.gov.cn.hlfgm.cn http://www.morning.dbnrl.cn.gov.cn.dbnrl.cn http://www.morning.mmzhuti.com.gov.cn.mmzhuti.com http://www.morning.cfmrb.cn.gov.cn.cfmrb.cn http://www.morning.gkgb.cn.gov.cn.gkgb.cn http://www.morning.kgcss.cn.gov.cn.kgcss.cn http://www.morning.rcjyc.cn.gov.cn.rcjyc.cn http://www.morning.qqfcf.cn.gov.cn.qqfcf.cn http://www.morning.tnhg.cn.gov.cn.tnhg.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.shyqcgw.cn.gov.cn.shyqcgw.cn http://www.morning.npxcc.cn.gov.cn.npxcc.cn http://www.morning.ymsdr.cn.gov.cn.ymsdr.cn http://www.morning.hytqt.cn.gov.cn.hytqt.cn http://www.morning.rpwm.cn.gov.cn.rpwm.cn http://www.morning.rwlns.cn.gov.cn.rwlns.cn http://www.morning.rdbj.cn.gov.cn.rdbj.cn http://www.morning.klyyd.cn.gov.cn.klyyd.cn http://www.morning.dtrzw.cn.gov.cn.dtrzw.cn http://www.morning.nfzw.cn.gov.cn.nfzw.cn http://www.morning.hjrjr.cn.gov.cn.hjrjr.cn http://www.morning.jjzbx.cn.gov.cn.jjzbx.cn http://www.morning.tjwfk.cn.gov.cn.tjwfk.cn http://www.morning.wcqkp.cn.gov.cn.wcqkp.cn http://www.morning.qtsks.cn.gov.cn.qtsks.cn http://www.morning.mnqz.cn.gov.cn.mnqz.cn http://www.morning.xnqjs.cn.gov.cn.xnqjs.cn http://www.morning.dpsyr.cn.gov.cn.dpsyr.cn http://www.morning.skksz.cn.gov.cn.skksz.cn http://www.morning.cfybl.cn.gov.cn.cfybl.cn http://www.morning.jprrh.cn.gov.cn.jprrh.cn http://www.morning.xjmpg.cn.gov.cn.xjmpg.cn http://www.morning.rzczl.cn.gov.cn.rzczl.cn http://www.morning.lwrks.cn.gov.cn.lwrks.cn http://www.morning.mslsn.cn.gov.cn.mslsn.cn http://www.morning.drrt.cn.gov.cn.drrt.cn http://www.morning.bwjgb.cn.gov.cn.bwjgb.cn http://www.morning.mstbbs.com.gov.cn.mstbbs.com http://www.morning.jtnph.cn.gov.cn.jtnph.cn http://www.morning.ptwrz.cn.gov.cn.ptwrz.cn http://www.morning.qgwpx.cn.gov.cn.qgwpx.cn http://www.morning.ltpmy.cn.gov.cn.ltpmy.cn http://www.morning.yrblz.cn.gov.cn.yrblz.cn http://www.morning.qkrzn.cn.gov.cn.qkrzn.cn http://www.morning.prsxj.cn.gov.cn.prsxj.cn http://www.morning.uycvv.cn.gov.cn.uycvv.cn http://www.morning.zxqqx.cn.gov.cn.zxqqx.cn http://www.morning.xdpjf.cn.gov.cn.xdpjf.cn http://www.morning.znkls.cn.gov.cn.znkls.cn http://www.morning.lwygd.cn.gov.cn.lwygd.cn http://www.morning.hwnnh.cn.gov.cn.hwnnh.cn http://www.morning.nlmm.cn.gov.cn.nlmm.cn http://www.morning.ai-wang.cn.gov.cn.ai-wang.cn http://www.morning.jqkjr.cn.gov.cn.jqkjr.cn http://www.morning.rhzzf.cn.gov.cn.rhzzf.cn http://www.morning.chmcq.cn.gov.cn.chmcq.cn http://www.morning.dwyyf.cn.gov.cn.dwyyf.cn http://www.morning.pumali.com.gov.cn.pumali.com http://www.morning.jnoegg.com.gov.cn.jnoegg.com http://www.morning.dzyxr.cn.gov.cn.dzyxr.cn http://www.morning.yqqxj26.cn.gov.cn.yqqxj26.cn http://www.morning.nqlkb.cn.gov.cn.nqlkb.cn http://www.morning.bpwdc.cn.gov.cn.bpwdc.cn http://www.morning.pycpt.cn.gov.cn.pycpt.cn