實(shí)現(xiàn)代碼
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。因?yàn)閠ransaction提供了原子級(jí)的執(zhí)行保證,在instance之外是無法提供的,也就是說必須同一個(gè)reids實(shí)例上
2、Redis Cluster集群 支持transaction,但是前提是transaction涉及的所有key都屬于同一hash slot,2^14-1 16383個(gè)槽位(總) - 解決辦法:
/**
* 嘗試獲取分布式鎖
*
* @param jedis Redis客戶端
* @param lockKey 鎖
* @param requestId 請求標(biāo)識(shí)
* @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(過期時(shí)間));
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
* 釋放分布式鎖
*
* @param jedis Redis客戶端
* @param lockKey 鎖
* @param requestId 請求標(biāo)識(shí)
* @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)指令插入一個(gè)鍵值對(duì),如果 Key 已經(jīng)存在,那么會(huì)返回 False,否則插入成功并返回 True。
SETNX 指令和數(shù)據(jù)庫的唯一索引類似,可以保證只存在一個(gè) Key 的鍵值對(duì),可以用一個(gè) Key 的鍵值對(duì)是否存在來判斷是否存于鎖定狀態(tài)。
EXPIRE 指令可以為一個(gè)鍵值對(duì)設(shè)置一個(gè)過期時(shí)間,從而避免了死鎖的發(fā)生。