大家好,我是李哥。
上次我們討論了在分散式系統下的快取架構體系,從瀏覽器快取到客戶端快取,再到CDN快取,再到反向代理快取,再到本地快取,再到分散式快取。整個鏈路中有非常多的快取。
在整個快取鏈路,存在各種各樣的問題,常見的問題有快取穿透、快取擊穿、快取雪崩、快取資料一致性問題等。不常見的問題有快取傾斜、快取阻塞、快取慢查詢、快取主從一致性問題、快取高可用、快取故障發現與故障恢復、叢集擴容收縮、大Key熱Key等等。
今天我們就來聊聊:
快取擊穿
老規矩,先看一下本文大綱:
快取擊穿是什麼?
我們知道,快取的工作原理是先從快取中獲取資料,如果有資料則直接返回給使用者,如果沒有資料則從慢速裝置上讀取實際資料並且將資料放入快取。就像這樣:
很多情況下,快取是存在失效時間的,如果一個快取資料失效,那麼請求就會透過快取層,打到慢裝置層,如果此時這個快取資料的訪問量很大(熱點資料),那麼慢裝置就要承受這波流量轟炸,扛不住可能就宕機了。
就像這樣:
這,便是
快取擊穿
。
快取擊穿強調單個key資料過期 + 高併發
快取擊穿的痛點有哪些?
從上面的流程圖來看,我們已經知道,最大的痛點就是慢裝置宕機了,後面引發一系列的問題相當的嚴重(整個慢裝置宕機,也就是說如果是單庫的服務,整個服務幾乎不可用;如果是涉及到分庫分表的,宕機幾個庫則雜湊在這些庫的請求幾乎不可用)。
其次,服務QPS瞬間下降。假設從快取取資料需要0。01s,從資料庫取資料需要1s,那麼1個執行緒在1s內原本能處理100個請求,現在只能處理1個請求,再假設1個服務的tomcat執行緒數為200,那麼原本1臺機器的QPS=執行緒數 * 每秒處理請求數=200 * 100=20000,現在則變成1臺機器的QPS=執行緒數 * 每秒處理請求數=200 * 1=200。
當併發過大(實際QPS大於200),那麼執行緒數不夠用了,使用者只能排隊等待執行緒釋放,這就是使用者反饋的:“什麼垃圾網站,卡死了!”。
快取擊穿的解決思路?
一旦發生快取擊穿的問題,看似是慢裝置扛不住的問題,實際罪魁禍首的並不是慢裝置,為什麼這麼說呢?
發生這種現象,我認為架構設計是不合理而導致的。在我看來,分為幾個階段治理快取擊穿。
如果有必要,請隔離
保證熱點資料存在快取中
防止熱點資料在快取中沒有
如果有必要,請隔離
隔離?怎麼隔離?
一般情況下,都是不需要隔離的,因為隔離的成本實在太大了,只有在超級大促活動的時候才會考慮這種隔離方案,比如618、雙11、雙12。
在這裡,這種隔離指的是普通環境與熱點環境進行熱點隔離,相互不影響。
對此,就算熱點環境的慢裝置扛不住這種瞬間流量宕機了,它也不影響我們正常環境的請求。
在我們的實際生產環境中,其實還存在其他的一些隔離方案,如:機房隔離,環境隔離,叢集隔離,程序隔離,執行緒隔離,資源隔離等。
保證熱點資料存在快取中
如果是這種高併發的資料,一定需要保證快取層能夠防住。
那麼問題來了,如何保證快取層能夠防住呢?
有兩種情況,快取阻塞或宕機導致快取擊穿,快取資料沒有導致快取擊穿。
如果是快取阻塞或宕機,這種情況下的解決方案是:
排查宕機的原因,排查慢查詢,最佳化慢查詢;
快取叢集最佳化或擴容;
如果是快取沒有資料,這種情況下的解決方案是:
熱點快取資料永不過期
使用定時任務定時重新整理快取資料與過期時間,保證快取資料存在
防止熱點資料在快取中沒有
在上面,我們已經分析了很多情況下,也做出了許多的佈防措施,當然,我們仍然是不能夠保證一定是永遠可用,仍然無法百分之百保證快取的資料是永不過期的。
所以,我們還需要考慮一個兜底的方案:假設快取沒有,那該怎麼辦?
如果不作為,一旦發生這種情況有可能服務和資料庫都宕機了,然後就。。。收拾書包就回家了。
所以一定要作為。
不知道大家還記不記得在上一篇文章中提到的,將「高併發轉變為低併發」,詳情可以看看之前的一片文章:
原理就是當快取層被擊破了,那麼會存在高併發的請求達到慢裝置,我們可以將「高併發轉變為低併發」。多數情況下我們都是使用分散式鎖。
而使用分散式鎖又有很多方案:
資料庫唯一鍵
資料庫悲觀鎖
基於redis的setnx原理
基於Redisson實現
基於Zookeeper實現
對於鎖,不是我們今天的主題,後續我們會推出鎖系列相關的文章,敬請期待。
可以關注我的公中號:李哥技術
總結
概念:快取擊穿指的是單個數據過期,加高併發的請求打到慢裝置後有可能導致慢裝置宕機。
現象:慢裝置宕機、服務QPS直線下降、使用者反饋網站慢
解決方案:
提高快取高可用;
保證熱點資料存在快取;
訪問慢裝置時由高併發轉低併發(一般使用分散式鎖);
感謝閱讀,
關注
我,助你成為架構師。
你的
分享、點贊、在看
將是對我最大的支援!