用redis分布式锁解决问题记录
在修改产品的时候,针对同一个产品多次进行修改的时候,出现了图片修改不符合预期的情况,后来发现是因为针对同一个产品在同一时刻同时调用几次修改图片的接口,会导致出现图片异常的情况。为了防止这种情况出现,首先想到了同步,但是修改任务是在多台服务器上部署,并且分片只是按照id分片。因此如果只是单纯的加锁也还是会出现在不同的机器上修改同一个产品ID的图片,那么想到了用redis分布式锁。
下面我们还是先贴代码然后讲述代码里面涉及的模块的作用,以及了解redis分布式锁的大体用法
// 修改 -如果修改图片 判断redis缓存里面是否存在对应itemId的分布锁
if (map.getString(WishConstant.MAIN_IMAGE_KEY) != null) {
boolean lock = false;
String uuidStr = String.valueOf(UUIDGenerator.create());
while (!lock) {
lock = systemJedisClient.tryGetDistributedLock(revise.getItemId(), uuidStr, 3 * 60 * 1000L);
}
// 获取到锁执行
rspWrapper = wishComponent.executeRequest(req);
systemJedisClient.releaseDistributedLock(revise.getItemId(), uuidStr);
} else {
rspWrapper = wishComponent.executeRequest(req);
}
/**
* 尝试获取分布式锁
*
* @param jedis
* Redis客户端
* @param lockKey
* 锁
* @param requestId
* 请求标识
* @param expireTime
* 超期时间
* @return 是否获取成功
*/
@Override
public boolean tryGetDistributedLock(String lockKey, String requestId, Long expireTime) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
jedis.close();
return LOCK_SUCCESS.equals(result);
}
/**
* 释放分布式锁
*
* @param jedis
* Redis客户端
* @param lockKey
* 锁
* @param requestId
* 请求标识
* @return 是否释放成功
*/
@Override
public boolean releaseDistributedLock(String lockKey, String requestId) {
Jedis jedis = jedisPool.getResource();
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
jedis.close();
return RELEASE_SUCCESS.equals(result);
}
其中jredis.set方法中的五个参数含义如下:
第一个为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的过期时间。