redis分布式锁实现

方案一:

​ 1、对key设值

​ 2、如果不存在,设值成功,则获取锁成功,业务完成,释放锁

​ 3、如果存在,则获取锁失败,等待锁或者放弃。

问题:

​ 1、如果在锁获取成功后,业务异常,导致释放锁失败,就会导致所有业务都被阻塞

方案二:

​ 1、在方案一的基础上,加入超时时间,为了防止获取锁之后挂掉导致超时时间设置失败,保证原子性,使用setnx命令问题

​ 1、业务异常,随后超时,其他业务获取到锁,然后上一个业务又释放锁,锁机制失败

方案三:

​ 1、加入业务id,是当前业务再进行释放,并且释放锁需要保证原子性,用lua脚本保证。

方案三为最终方案

流程图如下:

@RestController
@RequestMapping("/lock")
@Slf4j
public class LockController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    private DefaultRedisScript<Long> getRedisScript;

    private static final String KEY = "LOCK";

    @GetMapping("/lock")
    public String lock(String requestId) {
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(KEY, requestId, 5, TimeUnit.SECONDS);
        if (lock) {
            return "success";
        }
        return "failed";
    }

    @GetMapping("/unlock")
    public String unlock(String requestId) {
        Long lock = redisTemplate.execute(getRedisScript, Arrays.asList(KEY), new Object[]{requestId});
        if (lock > 0) {
            return "success";
        }
        return "failed";
    }

    @PostConstruct
    public DefaultRedisScript<Long> getscript() {
        getRedisScript = new DefaultRedisScript<Long>();
        getRedisScript.setResultType(Long.class);
        getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/lock.lua")));
        return getRedisScript;
    }
}

lua脚本

local key=KEYS[1];

local reqId =redis.call("get","LOCK")

if reqId==ARGV[1]
then
    return 1;
end
return 0;

results matching ""

    No results matching ""