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