springboot-note icon indicating copy to clipboard operation
springboot-note copied to clipboard

redis加错未采用原子操作

Open li-shaoke opened this issue 4 years ago • 3 comments

boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(businessKey, uniqueValue);
        if (!isSuccess) {
            throw new Exception("You can't do it,because another has get the lock =-=");
        }
        redisTemplate.expire(businessKey, annotation.lockTime(), TimeUnit.SECONDS);

这块redis操作,不是原子的,如果在设置过期时间前,突然宕机了,则加锁后的key将长时间存在, 应该用 Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit);

li-shaoke avatar Jan 06 '21 05:01 li-shaoke

已接纳你的建议,在代码中修复 Issue 1 & 2 的问题

Vip-Augus avatar Jan 06 '21 16:01 Vip-Augus

定时线程这一块感觉还有点问题, 当一个线程获锁之后执行完毕后另一个线程立即获锁,这个时候延时队列里面可能会存在两条数据一直在重试,当新的线程执行完毕后去释放锁

RedisLockDefinitionHolder redisLockDefinitionHolder = holderList.stream().filter(h -> businessKey.equals(h.getBusinessKey())).findFirst().orElse(null);
            if (redisLockDefinitionHolder != null && redisLockDefinitionHolder.getCurrentTread().equals(currentThread)) {
                // 请求结束后,强制删掉 key,释放锁
                redisTemplate.delete(businessKey);
                log.info("release the lock, businessKey is [" + businessKey + "]");
            }

上面这段逻辑可能会导致锁无法释放掉 建议释放锁的时候顺便将队列里面的该条数据删除

zhuquanzhen avatar Jan 26 '22 09:01 zhuquanzhen

定时线程这一块感觉还有点问题, 当一个线程获锁之后执行完毕后另一个线程立即获锁,这个时候延时队列里面可能会存在两条数据一直在重试,当新的线程执行完毕后去释放锁

RedisLockDefinitionHolder redisLockDefinitionHolder = holderList.stream().filter(h -> businessKey.equals(h.getBusinessKey())).findFirst().orElse(null);
            if (redisLockDefinitionHolder != null && redisLockDefinitionHolder.getCurrentTread().equals(currentThread)) {
                // 请求结束后,强制删掉 key,释放锁
                redisTemplate.delete(businessKey);
                log.info("release the lock, businessKey is [" + businessKey + "]");
            }

上面这段逻辑可能会导致锁无法释放掉 建议释放锁的时候顺便将队列里面的该条数据删除

是个很好的建议,已添加到代码中:commit log

Vip-Augus avatar Feb 10 '22 07:02 Vip-Augus