首页天道酬勤redis分布式锁使用场景,为什么需要分布式锁

redis分布式锁使用场景,为什么需要分布式锁

张世龙 05-03 20:58 58次浏览

适用场景

多个机器(多个过程)对同一数据进行修改,并要求该修改是原子的。 这里有两个限定。 (1)多个进程之间的竞争意味着JDK拥有的锁定无效。 )原子修正意味着数据有状态,修正前后有依存。

基于Redis的实践

锁定的实现

锁定密钥是目标数据的唯一密钥,value是锁定的预期超时时间点

首先,执行一次setnx命令,尝试获取锁定。 如果获取成功,则设置锁定的上次超时时间。 (防止当前进程在获取锁定后崩溃以释放锁定。 )获取锁定失败时,检查当前锁定是否超时,如果发现未超时,获取锁定失败。 如果发现锁定超时,即锁定超时时间小于或等于当前时间,则再次尝试获取锁定,在获取后判断当前超时时间是否等于以前的超时时间,如果相等,则当前客户端在排队等待

publicclassredisdistributionlock { privatestaticfinalloggerlogger=logger factory.getlogger (分布式. class //锁定的默认超时时间,20秒专用longdefaultexpiretime=20 * 1000; 私密性统计信息蓝牙连接=true; @resource(name='redistemplate ' ) private RedisTemplateString,stringredistemplateforgeneralize; /**锁定,锁定的默认超时时间为20秒* @ param resource * @ return */publicbooleanlock (string resource ) returnthis.lock desource } /** *锁定,同时锁定超时时间* @param key分布式锁定的key * @param expireTime单位为ms * @ return */publicbooleanlock (striricbolean * ) long expire time (logger.debug (redislockdebug,start.key:({},expiretime3360({}} ),key,expire time long now=longlockexpiretime=now expire time; //setnxbooleanexecuteresult=redistemplateforgeneralize.ops for value (.setifabsent (key,string.valueof ) lockexpipipid setNX.key:({},expiretime: ) },executeresult:({},key,key //成功解锁,并向key提供expire if (expire result ) 不获取(/)密钥,进程else (objectvaluefromredis=this.getkeywithretry (key,3 ); //获取锁定失败,同时对方解除锁定后,npeif(valuefromredis!=null(//已存在的锁定超时时间longoldexpiretime=long.parse long ((字符串) valueFromRedis ); Logger.debug(redislockdebug,keyalreadyseted.key:({},oldExpireTime: ) }、key、oldexpiretime ) )//锁重新拔插锁定if (old expire time=now )。

logger.debug("redis lock debug, lock time expired. key:[{}], oldExpireTime:[{}], now:[{}]", key, oldExpireTime, now); String valueFromRedis2 = redisTemplateForGeneralize.opsForValue().getAndSet(key, String.valueOf(lockExpireTime)); long currentExpireTime = Long.parseLong(valueFromRedis2); //判断currentExpireTime与oldExpireTime是否相等 if(currentExpireTime == oldExpireTime){ //相等,则取锁成功 logger.debug("redis lock debug, getSet. key:[{}], currentExpireTime:[{}], oldExpireTime:[{}], lockExpireTime:[{}]", key, currentExpireTime, oldExpireTime, lockExpireTime); redisTemplateForGeneralize.expire(key, finalDefaultTTLwithKey, TimeUnit.SECONDS); return true; }else{ //不相等,取锁失败 return false; } } } else { logger.warn("redis lock,lock have been release. key:[{}]", key); return false; } } return false; } private Object getKeyWithRetry(String key, int retryTimes) { int failTime = 0; while (failTime < retryTimes) { try { return redisTemplateForGeneralize.opsForValue().get(key); } catch (Exception e) { failTime++; if (failTime >= retryTimes) { throw e; } } } return null; } /** * 解锁 * @param key * @return */ public boolean unlock(String key) { logger.debug("redis unlock debug, start. resource:[{}].",key); redisTemplateForGeneralize.delete(key); return Success; }}

自定义注解使用分布式锁

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface RedisLockAnnoation { String keyPrefix() default ""; /** * 要锁定的key中包含的属性 */ String[] keys() default {}; /** * 是否阻塞锁; * 1. true:获取不到锁,阻塞一定时间; * 2. false:获取不到锁,立即返回 */ boolean isSpin() default true; /** * 超时时间 */ int expireTime() default 10000; /** * 等待时间 */ int waitTime() default 50; /** * 获取不到锁的等待时间 */ int retryTimes() default 20;}

实现分布式锁的逻辑

@Component@Aspectpublic class RedisLockAdvice { private static final Logger logger = LoggerFactory.getLogger(RedisLockAdvice.class); @Resource private RedisDistributionLock redisDistributionLock; @Around("@annotation(RedisLockAnnoation)") public Object processAround(ProceedingJoinPoint pjp) throws Throwable { //获取方法上的注解对象 String methodName = pjp.getSignature().getName(); Class<?> classTarget = pjp.getTarget().getClass(); Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes(); Method objMethod = classTarget.getMethod(methodName, par); RedisLockAnnoation redisLockAnnoation = objMethod.getDeclaredAnnotation(RedisLockAnnoation.class); //拼装分布式锁的key String[] keys = redisLockAnnoation.keys(); Object[] args = pjp.getArgs(); Object arg = args[0]; StringBuilder temp = new StringBuilder(); temp.append(redisLockAnnoation.keyPrefix()); for (String key : keys) { String getMethod = "get" + StringUtils.capitalize(key); temp.append(MethodUtils.invokeExactMethod(arg, getMethod)).append("_"); } String redisKey = StringUtils.removeEnd(temp.toString(), "_"); //执行分布式锁的逻辑 if (redisLockAnnoation.isSpin()) { //阻塞锁 int lockRetryTime = 0; try { while (!redisDistributionLock.lock(redisKey, redisLockAnnoation.expireTime())) { if (lockRetryTime++ > redisLockAnnoation.retryTimes()) { logger.error("lock exception. key:{}, lockRetryTime:{}", redisKey, lockRetryTime); throw ExceptionUtil.geneException(CommonExceptionEnum.SYSTEM_ERROR); } ThreadUtil.holdXms(redisLockAnnoation.waitTime()); } return pjp.proceed(); } finally { redisDistributionLock.unlock(redisKey); } } else { //非阻塞锁 try { if (!redisDistributionLock.lock(redisKey)) { logger.error("lock exception. key:{}", redisKey); throw ExceptionUtil.geneException(CommonExceptionEnum.SYSTEM_ERROR); } return pjp.proceed(); } finally { redisDistributionLock.unlock(redisKey); } } }}
基于数据库实现分布式锁,什么场景用到分布式锁