1 屈服于現實的磁盤
MQ都使用磁盤來存儲消息。這樣伺服器下電也不會丢資料。絕大多數用于生産系統的伺服器,都會使用多塊磁盤組成磁盤陣列,這樣即使其中的一塊異常,也可把資料從其他磁盤中恢複。
另外磁盤也便宜,就可用較低成本,存儲海量消息。是以,不僅僅是MQ,幾乎所有存儲系統的資料,都需儲存到磁盤。
但磁盤讀寫很慢。SSD可讀寫幾千次/s,若程式在處理業務請求時直接讀寫磁盤,假設處理每次請求需要讀寫3~5次,即使每次請求資料量不大,程式最多也就能處理1000次/s左右請求。
而記憶體随機讀寫速度是磁盤10萬倍!記憶體作為緩存來加速程式通路速度,是所有高性能系統都會采用的方案。
緩存思想簡單,就是把低速存儲的資料,複制一份放到高速存儲,加速資料通路。使用也簡單
在做業務系統時,在一些執行較慢方法上加個@Cacheable
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SN4IGMxIzM3kTY3MDO4MTYkRjN0EWZlRWOkhTMmJWZ58CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
2 緩存最佳實踐
采用@Cacheable注解緩存的命中率如何?
怎樣才能提高緩存命中率?
緩存是否總能傳回最新的資料?
如果緩存傳回了過期的資料該怎麼辦?
隻讀緩存 VS 讀寫緩存
唯一差別:更新資料時,是否經過緩存。
Kafka使用的PageCache,是個典型的讀寫緩存。os會利用系統空閑實體記憶體給檔案讀寫做緩存,這緩存叫做PageCache。應用程式在寫檔案時,os會先把資料寫入PageCache,成功寫進後,對于使用者代碼,寫入就結束了。
然後,os再異步更新資料到磁盤。應用程式在讀檔案時,os是先嘗試從PageCache查資料,找到就直接傳回,找不到會觸發一個缺頁中斷,然後os把資料從檔案讀取到PageCache,再傳回給應用程式。
資料寫到PageCache後,并不是同時寫到磁盤,期間有個延遲。
os可保證即使程式異常退出,os也會把這部分資料同步到磁盤。但若伺服器都突然掉下電,這部分資料就丢了。
讀寫緩存的設計,本身就不可靠,犧牲資料一緻性換取性能。當然,程式可以調用sync等系統調用,強制作業系統立即把緩存資料同步到磁盤檔案中去,但是該同步過程很慢,也失去了緩存的意義。
寫緩存實作非常複雜。應用程式不停更新PageCache資料,os需記錄哪些資料變化,同時還要在另外一個線程,把緩存中變化的資料更新到磁盤。
在提供并發讀寫同時異步更新資料,這過程要保證資料一緻性,且有非常好性能,可為強人鎖男。
是以不推薦使用讀寫緩存。
那為什Kafka可使用PageCache提升性能?
這由MQ特點決定。
MQ讀寫比例大緻1:1,因大部分MQ都是一收一發。這種讀寫比例,隻讀緩存既無法給寫加速,讀加速也有限,并不能提升多少性能。
Kafka并不是隻靠磁盤保證資料可靠性,它更依賴在不同節點上的多副本保證資料可靠性,這樣即使某伺服器掉電丢失一部分檔案内容,也可從其他節點找到正确資料,不會丢消息。
而且PageCache讀寫緩存是os實作,Kafka隻要按照正确姿勢使用即可,不涉及實作複雜度問題。是以,Kafka其實在設計上,充分利用PageCache讀寫緩存的優勢,且規避了PageCache一些劣勢,達到很好效果。
和Kafka一樣,大部分其他MQ,也會采用讀寫緩存加速消息寫入,隻是實作方式不同。
不同于MQ,大部分業務類程式,讀寫比都是嚴重不均衡,一般讀頻率遠高于寫數,一般都幾倍到幾十倍。使用隻讀緩存來加速系統才是明智選擇。
設計隻讀緩存又該考慮哪些問題呢?
維護緩存資料時效性
對隻讀緩存,緩存中資料源隻有一個途徑:磁盤。當資料需更新時,磁盤資料和緩存副本都需更新。在分布式系統中,除非是使用事務(性能差)或者一些分布式一緻性算法(複雜)保證資料一緻性。否則,由于節點當機、網絡傳輸故障等,是無法保證緩存中資料和磁盤中的資料完全一緻的。
若出現資料不一緻,資料一定是以磁盤上那份拷貝為準的。
需解決問題:盡量讓緩存資料與磁盤資料保持同步。
何時更新緩存資料
在更新磁盤資料同時,更新下緩存資料不就行?想法沒任何問題,緩存中資料會一直保持最新。但在并發環境,實作起來不太容易