概念介紹
熱點Key
產生的背景
用戶消費的數據遠大於生產的數據(熱賣商品、熱點新聞、熱點評論、熱門明星直播)。
對於電商網站中,我們經常可以會遇到熱門商品的搶購或者秒殺場景以及事先經過廣告投放等措施進行定向引流,這樣就會導致某個熱賣商品在短時間內涌入大量流量。
比如,雙十一期間某些熱門商品的降價促銷,當這其中的某一件商品被數萬次點擊瀏覽或者購買時,會形成一個較大的需求量,這種情況下就會造成熱點問題。
導致的問題及解決方案
熱點Key產生問題的原因
請求到的分片過於集中,超過單台Server的性能極限。
在服務端讀數據進行訪問時,往往會對數據進行分片切分。此過程中會在某一主機Server上對相應的Key進行訪問,當訪問超過Server極限時,就會導致熱點 Key 問題的產生。
熱點Key的危害
- 流量集中,達到物理網卡上限。
- 請求過多,緩存分片服務被打垮。
- DB 擊穿,引起業務雪崩。
解決方式
- 服務端緩存:即將熱點數據緩存至服務端的內存中。
- 備份熱點Key:即將熱點Key+隨機數,隨機分配至Redis其他節點中。這樣訪問熱點key的時候就不會全部命中到一台機器上了。
大Key
Redis使用過程中經常會有各種大key的情況, 比如單個簡單的key存儲的value很大。
由於redis是單線程運行的,如果一次操作的value很大會對整個redis的響應時間造成負面影響,導致IO網絡擁塞。
解決方案
將整存整取的大對象,分拆為多個小對象。可以嘗試將對象分拆成幾個key-value, 使用multiGet獲取值,這樣分拆的意義在於分拆單次操作的壓力,將操作壓力平攤到多個redis實例中,降低對單個redis的IO影響;
一次實戰優化過程
問題簡介
在電商網站的一次營銷事件中,通過相關的引流操作,將大量流量在指定時間引流到了商品搶購頁面,在搶購頁中涉及到的幾個后台接口,其中有一兩個都會去查商品信息。
在商品模塊對外暴露了一個對條件查詢的接口,在上述的場景下就會短時間高頻次的調用這個接口。
根據這個場景發現,商品數據,在活動期間會有很大的訪問量,這是一個熱點Key。另外由於前期錯誤的設置導致了這個熱點Key又是一個大Key。
所以我們的優化過程就是按照如果解決掉熱點Key和大Key的這兩個問題進行的。之前並沒有上述的概念,都是摸着石頭過河,漸漸地思路才清晰起來。
解決過程
第一版
直接按條件查詢數據庫。
public List<ReturnObject> version1(QueryCriterionDto queryDto) { // 1.直接按照條件查詢數據庫 // 2.將查詢到的結果返回 }
第二版
將全量數據緩存到Redis中,查出數據后再進行過濾返回。
public List<ReturnObject> version2(QueryCriterionDto queryDto) { // 1.先嘗試從緩存中查詢全量數據 // 2.如果不存在,則從數據庫中把全量數據出,並緩存到Redis中 // 3.對全量數據進行過濾篩選 // 4.返回最終的結果 }
第三版:
仍然將全量數據緩存到Redis中,但是只緩存必要的數據,比如過濾條件,從緩存中拿出全部的數據后,進行過濾后列出需要返回的對象唯一id,再根據這一批唯一id去緩存中查單個的對象出來,最后拼成List返回。
public List<ReturnObject> version3(QueryCriterionDto queryDto) { // 1.先嘗試從緩存中查詢全量數據(這里的全量數據指的不是ReturnObject,而是另外一個只用於過濾篩選的簡單對象) // 2.如果不存在,則從數據庫中把全量數據出,轉成簡單對象,並緩存到Redis中 // 3.簡單對象中包含所有的過濾條件,過濾后得到一組最終的idList(每個id在緩存中對應一個ReturnObject) // 4.根據上面得到的idList,然后循環get取出最后的返回結果 }
第四版
將每次的獲取單個對象的get操作,調整成mget這樣就可以一次操作取出多個對象出來,然后再跟查詢的keys比較一下size,如果少的話,再嘗試從庫中查一次,把少的數據補上。
public List<ReturnObject> version4(QueryCriterionDto queryDto) { // 1.先嘗試從緩存中查詢全量數據(這里的全量數據指的不是ReturnObject,而是另外一個只用於過濾篩選的簡單對象) // 2.如果不存在,則從數據庫中把全量數據出,轉成簡單對象,並緩存到Redis中 // 3.簡單對象中包含所有的過濾條件,過濾后得到一組最終的idList(每個id在緩存中對應一個ReturnObject) // 4.對上面得到的idList進行分組比如每50個keys作為一組,然后使用multiGet一次獲取50個對象,從而降低redis調用次數 // 5.針對從redis中沒有取到的對象,再嘗試從庫中查詢,進行填補 // 6.返回最終的對象 }
參考文章:
https://cloud.tencent.com/dev...
https://blog.csdn.net/youanyy...
https://segmentfault.com/a/11...
如果對您有幫助,請不要忘了給翎野君點贊。