redis分佈式鎖:setNx自定義鎖
redis分佈式鎖原理:視頻教程:https://edu.csdn.net/course/play/25604
set命令:如果 key 已經持有其他值, SET 就覆寫舊值
setEX命令:成功返回ok,失敗返回no,如果 key 已經存在, SETEX 命令將覆寫舊值,可以設置有效時間
setNX命令:成功返回1,失敗返回0,若給定的 key 已經存在,則 SETNX 不做任何動做
流程
流程說明:
- A、B線程同時通過setEX,setNX命令用同一個key去添加redis,由於redis是單線程的,只能有一個線程成功返回ok, A成功去處理業務邏輯,B失敗但是一直在循環添加redis
- A執行業務邏輯完成後刪除redis的key,B就可以添加redis成功,去處理相應的業務邏輯
- redis鎖一定要設置過期時間,防止A拿到鎖,當時還沒釋放程序中途出了異常,導致B一直拿不到鎖,程序卡死
自定義鎖代碼實現:(以下爲只用setNX實現方式)
- lockkey可以是你的請求系統+請求流水等字段拼接的唯一的標識,過期時間比執行業務邏輯時間往上加一些就可以(一定要確保大於業務邏輯執行時間,否則A線程執行del時會因爲自己的鎖已經自動刪除,而去把B線程的鎖給刪掉)
- 每個線程的key對應的value都不一樣,在刪除時可以根據value判斷是否是自己的redis,避免把其他線程的key刪除
- 如果A線程做刪除操作時,通過value發現鎖是B線程的,這時A線程應該回滾並拋出異常,防止業務重複執行
業務代碼使用自定義鎖
// 生成redisKey:根據自己業務去拼接一個不唯一的字符串作爲Key
String lockKey = RedisKeyConstant.REDIS_SENTINEL_PREFIX + KeysGeneratorUtil.createRedisKey(
InfoBO.getRequestSystem(), InfoBO.getEquityNo());
try {
// 獲取鎖
boolean lockResult = redisUtil.getLock(lockKey, 5000L);
if (!lockResult) {
log.error("獲取鎖失敗,redisKey:{}",lockKey);
throw new EquityServiceException("錯誤碼");
}
} catch (Exception e) {
log.error("獲取鎖異常,redisKey:{}",lockKey);
throw new ServiceException("錯誤碼");
}
// 加鎖成功,處理業務代碼
try {
// 加鎖成功,處理業務代碼
} catch (Exception e ) {
// 執行業務代碼異常
} finally {
// 根據鎖Key釋放redis鎖:實際上是刪除的setNx中的key
redisUtil.deleteKey(lockKey);
}