实现代码
Jedis jedis = pool.getResource();
try {
Transaction tx = jedis.multi();
try {
tx.get("XXX");
tx.set("YYY","ZZZ")
resp = tx.exec();
} finally { tx.close(); }
} catch (Exception e) {
// TODO
}finally { jedis.close(); }
- 问题:
1、客户端分片和中间件(我们目前使用的codis)分片均不支持transaction。因为transaction提供了原子级的执行保证,在instance之外是无法提供的,也就是说必须同一个reids实例上
2、Redis Cluster集群 支持transaction,但是前提是transaction涉及的所有key都属于同一hash slot,2^14-1 16383个槽位(总) - 解决办法:
/**
* 尝试获取分布式锁
*
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST(NX), SET_WITH_EXPIRE_TIME(PX), expireTime(过期时间));
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* 释放分布式锁
*
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
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));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
解释
使用 SETNX(set if not exist)指令插入一个键值对,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。
SETNX 指令和数据库的唯一索引类似,可以保证只存在一个 Key 的键值对,可以用一个 Key 的键值对是否存在来判断是否存于锁定状态。
EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了死锁的发生。
网友评论