Redis分佈式鎖的實現以及工具類

1、應用場景:

  本文應用的場景爲在查詢數據時,發現數據不存在此時就須要去查詢數據庫而且更新緩存,此時可能存在高併發的請求同時打在數據庫上,而針對這種狀況必需要給這些請求加鎖,故而採用了分佈式鎖的方式。(固然分佈式鎖的應用場景較多,我只是針對本人工做的業務場景作了對應的處理)redis

2、Redis鎖的工具類:

/**
 * Redis分佈式鎖
 */
@Component
public class RedisLock {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 加鎖
     * @param key
     * @param value 當前時間+超時時間
     * @return
     */
    public boolean lock(String key, String value) {
    
        if(redisTemplate.opsForValue().setIfAbsent(key, value)) {//至關於SETNX,setIfAbsent方法設置了爲true,沒有設置爲false
            return true;
        }
        //假設currentValue=A   接下來併發進來的兩個線程的value都是B  其中一個線程拿到鎖,除非從始至終全部都是在併發(實際上這中狀況是不存在的),只要開始時有數據有前後順序,則分佈式鎖就不會出現「多賣」的現象
        String currentValue = String.valueOf(redisTemplate.opsForValue().get(key));
        //若是鎖過時  解決死鎖
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //獲取上一個鎖的時間,鎖過時後,GETSET將原來的鎖替換成新鎖
            String oldValue = String.valueOf(redisTemplate.opsForValue().getAndSet(key, value));
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }

        return false;//拿到鎖的就有執行權力,拿不到的只有從新再來,從新再來只得是讓用戶手動繼續搶單
    }

    /**
     * 解鎖
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = String.valueOf(redisTemplate.opsForValue().get(key));
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

}

3、業務代碼:

JSONArray allApplyForms = null;
if (this.redisActivityService.exists(RedisKeys.ApplyFormList_KEY+String.valueOf(activityId))) {
  // 若是redis緩存在有該活動的做品列表,則直接從redis中獲取 Object allApplyFormsJSON = this.redisActivityService.get(RedisKeys.ApplyFormList_KEY+String.valueOf(activityId)); allApplyForms = JSONArray.fromObject(allApplyFormsJSON.toString());
}
else { long time = System.currentTimeMillis()+(20 * 1000); if(!redisLock.lock("ApplyFormListLock", String.valueOf(time))){ return CORSUtil.getResult(0, "當前訪問量大,刷新一下", null, callback); } allApplyForms = this.activityService.getAllApplyForms(activityId, thumbnailWidth,thumbnailHeight, contentCode, formTitle, flag,formModel); if(allApplyForms ==null){ return CORSUtil.getResult(0, "沒有對應的參選表單", null,callback); } this.redisActivityService.set(RedisKeys.ApplyFormList_KEY+String.valueOf(activityId), allApplyForms.toString(),Long.parseLong(systemConfigService.getConfig("redisOverTime"))+new Random().nextInt(200)); redisLock.unlock("ApplyFormListLock", String.valueOf(time)); }

1,從redis中獲取對應的數據,若是獲取到直接返回,若是沒有就走接下來的加鎖代碼數據庫

2,若是加鎖不成功,則說明已經有請求進入到後面的業務邏輯,這時候就直接返回給客戶端,等待緩存

3,若是加鎖成功,則查詢數據並更新Redis,最後再釋放鎖併發

相關文章
相關標籤/搜索