使用Redis做分布式锁遇到的一个问题

加锁代码很简单,lockName不存在时设值value,一分钟时间过期,redis可以保证原子性

public boolean lock(String lockName, String value) {
		return valueOperations.setIfAbsent(lockName, value, Duration.ofSeconds(60));
	}

解锁用redis脚本实现原子性,lockName和value作为脚本参数

public boolean unlock(String lockName, String value) {
        Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockName), "value");
        return 1 == result;
	}

get lockName 的值与value比较,相等则删除(解锁),否则直接返回解锁成功

@Bean
    public DefaultRedisScript<Long> unlockScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 1 end");
        redisScript.setResultType(Long.class);
        return redisScript;
    }

测试代码

		String lockName = "myLockName";
		String lockValue = "myLockValue";
		System.out.println("lock result: " + redisLock.lock(lockName, lockValue));
		System.out.println("unlock result: " + redisLock.unlock(lockName, lockValue));
lock result: true
unlock result: true

似乎加锁和解锁都成功了,但是redis里锁依然存在,没有被删除
使用Redis做分布式锁遇到的一个问题_第1张图片
原因在于springboot配置RedisTemplate采用了json序列化:

template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

所以加锁时myLockName的值不是myLockValue而是"myLockValue",而解锁时执行的redis脚本的参数是不带双引号的,导致直接走到else逻辑返回1。

你可能感兴趣的:(java,redis,分布式锁)