第一種,看看自己是否已經入坑了。
//判斷redis快取是否有資料
if(!jedis.exists("testlocklistv_1"))else
system.out.println(jedis.get("testlocklistv_1"));//快取有資料直接返回快取資料
很多同學都是判斷是否存在相同的key,如果不存在,就訪問資料庫,然後把資料放入快取中,然後返回。
實際多執行緒執行時,會列印如下
多執行緒情況下多次擊穿快取,直接訪問資料庫
多執行緒情況下多次擊穿快取,直接訪問資料庫
多執行緒情況下多次擊穿快取,直接訪問資料庫
多執行緒情況下多次擊穿快取,直接訪問資料庫
多執行緒情況下多次擊穿快取,直接訪問資料庫
多執行緒情況下多次擊穿快取,直接訪問資料庫
答案很簡單,因為多個執行緒同時執行jedis.exists("testlocklistv_1")這一段**時,快取中確實沒有資料。然後都執行查詢資料庫,放入快取中。這樣請求還是直接通過資料庫拿資料
第二種經過改造,我們知道redis是可以實現分布式鎖的jedis.setnx(key,value); 那我們改造一下,通過分布式鎖來解決穿透的問題。
改造如下:
jedis jedis = jedispool.getresource();
//判斷redis快取是否有資料
if(!jedis.exists("testlocklistv_2"))
}else
system.out.println(jedis.get("testlocklistv_2"));//快取有資料直接返回快取資料
高併發先,列印結果
多執行緒情況下多次擊穿快取,直接訪問資料庫
資料庫中返回的查詢記錄
多執行緒情況下多次擊穿快取,直接訪問資料庫
資料庫中返回的查詢記錄
結果發現,即使加了鎖,我們還是穿透了快取,直接訪問資料庫了,雖然請求較少。仔細分析原因是因為,多個執行緒同時進入後判斷是否有相同key,由於第一次訪問,redis中是沒有相應的key的,但是多個執行緒又同時去拿鎖,此時雖然只有乙個執行緒能獲取鎖成功,進入查詢資料庫,然後把資料放入快取。但是會出現剛放完快取,釋放所。同一時間內有幾個執行緒剛通過if(!jedis.exists("testlocklistv_2"))判斷是否有次鍵值對的判斷,然後也重新獲取鎖,也訪問資料庫,儲存資料到快取。這樣也出現了擊穿快取,直接訪問資料庫中。
第三種,上面分析完畢前面幾種情況以後。
歸納一下修改後的邏輯:
1.查詢快取,如果快取存在,返回結果
2.快取不存在,查詢資料庫
3.爭奪分布式鎖
4.成功獲得鎖,再次判斷快取的存在
5.如果快取仍舊不存在,把查詢資料庫的結果迴圈放入快取
6.釋放分布式鎖
這種二次判斷存在性的機制有乙個專門的名字,叫做雙重檢測
**如下:
jedis jedis = jedispool.getresource();
//判斷redis快取是否有資料
if(!jedis.exists("testlocklistv_3"))else
system.out.println(jedis.get("testlocklistv_3"));//快取有資料直接返回快取資料
}}else
多執行緒下執行結果:
多執行緒情況下多次擊穿快取,直接訪問資料庫
資料庫中返回的查詢記錄
非常好已經只訪問一次資料庫,並且沒有穿透快取。
原理,因為所有的執行緒下都只能有乙個執行緒獲取鎖,如果獲取鎖以後,更新完快取以後。另外乙個快取進入,也要判斷是否已經存在key,沒有key才會查詢資料庫更新。只要執行過獲取鎖的操作完畢以後,肯定會存入快取的。所以這種方案是不會有擊穿快取的情況的。
幾點補充:
1.文中所使用的分布式鎖,其實並不是「正宗」的分布式鎖,當執行緒爭奪鎖失敗的時候,會直接返回查詢db的結果,而不會依靠自旋機制來等鎖。
2.為什麼優惠券列表的資訊要使用list型別來存入快取,而不是把整個列表存為乙個很長的json字串?這是由於業務需要,使用list在某些情況下更方便對單個優惠券資訊進行修改(lset指令)。
3.為什麼優惠券列表的資訊不使用redis的set或者hash資料型別來儲存,實現自動去重呢?對於set型別,去重前需要對比整個字串是否完全相同,而每一張優惠券是乙個較長的json字串,對比的效率會比較低。使用hash倒是可以實現高效的去重,但並未在根本上解決重複更新的問題。
Redis在高併發下存在的問題
描述 在某些特定環境下,無論是先更新redis還是更新資料庫,兩者的資料都有可能不一致。解決方案1 雙寫模式 解決方案2 失效模式 最終解決方案 無論是雙寫模式還是失效模式,都會導致快取的不一致問題。即多個例項同時更新會出事,怎麼辦?如果是使用者緯度資料 訂單資料 使用者資料 這種併發機率非常小,不...
springboot下redis高併發下的快取穿透
public responsebody string getclassesbyid pathvariable id integer id return redistemplate.opsforvalue get classes 從redis中拿 這樣看單機條件下沒有問題但是高併發下還是會存在多個使用...
高併發下restTemplate的錯誤分析
org.apache.http.conn.connectionpooltimeoutexception timeout waiting for connection此問題很明顯是連線等待超時,而且是從連線池中獲取的連線。那麼就有乙個很詫異的問題,這裡哪來的連線池呢?然後我去跟蹤resttemplat...