天天看點

Java高性能系統緩存的最佳實踐(上)1 屈服于現實的磁盤2 緩存最佳實踐維護緩存資料時效性        何時更新緩存資料

1 屈服于現實的磁盤

MQ都使用磁盤來存儲消息。這樣伺服器下電也不會丢資料。絕大多數用于生産系統的伺服器,都會使用多塊磁盤組成磁盤陣列,這樣即使其中的一塊異常,也可把資料從其他磁盤中恢複。

另外磁盤也便宜,就可用較低成本,存儲海量消息。是以,不僅僅是MQ,幾乎所有存儲系統的資料,都需儲存到磁盤。

但磁盤讀寫很慢。SSD可讀寫幾千次/s,若程式在處理業務請求時直接讀寫磁盤,假設處理每次請求需要讀寫3~5次,即使每次請求資料量不大,程式最多也就能處理1000次/s左右請求。

而記憶體随機讀寫速度是磁盤10萬倍!記憶體作為緩存來加速程式通路速度,是所有高性能系統都會采用的方案。

緩存思想簡單,就是把低速存儲的資料,複制一份放到高速存儲,加速資料通路。使用也簡單

在做業務系統時,在一些執行較慢方法上加個@Cacheable

Java高性能系統緩存的最佳實踐(上)1 屈服于現實的磁盤2 緩存最佳實踐維護緩存資料時效性        何時更新緩存資料

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,大部分業務類程式,讀寫比都是嚴重不均衡,一般讀頻率遠高于寫數,一般都幾倍到幾十倍。使用隻讀緩存來加速系統才是明智選擇。

設計隻讀緩存又該考慮哪些問題呢?

維護緩存資料時效性        

對隻讀緩存,緩存中資料源隻有一個途徑:磁盤。當資料需更新時,磁盤資料和緩存副本都需更新。在分布式系統中,除非是使用事務(性能差)或者一些分布式一緻性算法(複雜)保證資料一緻性。否則,由于節點當機、網絡傳輸故障等,是無法保證緩存中資料和磁盤中的資料完全一緻的。

若出現資料不一緻,資料一定是以磁盤上那份拷貝為準的。

需解決問題:盡量讓緩存資料與磁盤資料保持同步。

何時更新緩存資料

在更新磁盤資料同時,更新下緩存資料不就行?想法沒任何問題,緩存中資料會一直保持最新。但在并發環境,實作起來不太容易