在高併發場景中,爲了減少數據庫的壓力,咱們會引入緩存。java
一個簡單的查詢操做,以下所示:數據庫
僞代碼以下:緩存
public Object getObjById(Integer objId){ String key = "Lock:Obj:"+objId; //先從緩存中獲取數據 Obj obj= DistributeCacheUtil.get(key); if(obj!=null){ //若是緩存中存在則直接返回緩存中數據 return obj; } //若是緩存中不存在該數據,從數據庫中查詢 obj= DbUtil.getObjById(objId); if(obj!=null){ //若是 數據庫中存在,將查詢出來的數據放入緩存,設置過時時間 DistributeCacheUtil.set("Lock:Obj:"+objId,obj,1000); } //返回 return obj; }
但是這裏出現一個問題,好比秒殺場景或者一些其餘場景,某一時刻開始進行秒殺,這時候大量的請求會請求到後臺,若是沒有事先進行數據緩存預熱,這個時候緩存裏是沒有數據的,那麼大量的請求回去查詢數據庫,這樣整個緩存就失去意義了。併發
如何解決?分佈式
1.提早進行數據緩存預熱,好比項目啓動時,或者定時器,先將數據查詢出來放到緩存。高併發
2.加入鎖,具體僞代碼邏輯以下所示:線程
public Object getObjById(Integer objId){ String key = "Lock:Obj:"+objId; //先從緩存中獲取數據 Obj obj= DistributeCacheUtil.get(key); if(obj!=null){ //若是緩存中存在則直接返回緩存中數據 return obj; } //若是緩存中不存在該數據 //加鎖,這裏分爲阻塞鎖和非阻塞鎖 if(DistributeCacheUtil.tryLock(key)){ //進鎖之後先從緩存中獲取數據 //若是第一個線程持鎖後,加數據加載進緩存後,後來進來的線程能夠直接從緩存中拿 obj= DistributeCacheUtil.get(key); if(obj!=null){ //若是緩存中存在則直接返回緩存中數據 return obj; } //若是緩存中仍是不存在,再從數據庫中拿 obj= DbUtil.getObjById(objId); if(obj!=null){ //若是數據庫中存在,將查詢出來的數據放入緩存,設置過時時間 DistributeCacheUtil.set("Lock:Obj:"+objId,obj,1000); } //返回 return obj; }else{ //這裏是處理分佈式鎖爲非阻塞鎖的狀況,若是是阻塞式鎖,這裏就沒必要要處理了 //這裏根據實際狀況處理,好比直接返回null(秒殺場景,直接返回賣完了) //或者休眠1秒鐘,再從緩存中拿 //或者自旋去查詢緩存 return null; } }