如果沒有緩存,當打開一個有大量內容的頁面的時候,將會變得十分的卡頓,因為沒有緩存,每次打開頁面的時候都進行全部加載,而導致頁面打開速度變得很慢,給用戶的體驗也就十分的不愉快了。有了緩存,則會讓原本打開很慢的頁面變得“秒開”。那么緩存除了能夠加速數據的訪問外,還有什么作用?本文將為您詳細介紹 java 緩存的運放方式、緩存的類別以及緩存會出現的問題。
一、緩存能用來做什么?
大多數人對于緩存的理解就是,當我們打開一個頁面或是一個APP,當它們打開的速度很慢的時候,都會想到引入緩存,這樣打開就會更快。
從技術這一方面來說,緩存之所以能夠提高訪問速度,是因為緩存是基于內存去建立的。而內存的讀寫速度相對于硬盤是快很多的,所以用內存代替硬盤作為讀寫的介質,自然就會大大提升了訪問數據的速度。
使用緩存的過程大致如下:
二、運用方式:預讀取和延遲寫
除了上述的過程之外,緩存另外兩個重要的運用方式:預讀取和延遲寫。
2.1 預讀取
從字面意思來看就是預先讀取,實際意義也確實如此。預讀取就是提前把將要讀取的數據載入,也就是在系統中把硬盤中的一部分數據提前加載到內存中,然后對外提供服務。
那么這么做的意義是什么?
因為有些系統一旦啟動就會有數以萬計的請求涌進來,假設讓這些請求直接打到數據庫上,非常大的可能就是會讓數據庫的壓力劇增,數據庫就會被掛掉,而導致無法正常響應。
預讀取就是為了解決這樣的問題。
2.2 延遲寫
如果說預讀取就是在數據出口加一道緩沖區(qū),那么延遲寫則是在數據入口加一道緩沖區(qū)。
由于數據庫的寫入速度要比讀取速度慢,因此在寫入的時候就需要一系列的保證數據正確性的機制。所以,要想提升寫入速度,或是分庫分表,或是通過緩存加一道緩沖,再一次性批量寫入磁盤。引入分庫分表的復雜度遠大于引入緩存,一般都是優(yōu)先考慮引入緩存的方案。
這種緩存方案就是延遲寫,它是預先將準備寫入磁盤或數據庫的數據,暫時寫入到內存,然后返回成功,再定時分批將內存中的數據寫入到磁盤。
三、哪些可以加緩存?
在緩存之前需要考慮我們要緩存的是什么?符合什么樣特點的數據才需要加緩存?因為緩存算是一個額外的成本投入,所以加了緩存要體現它的價值。
先引入衡量數據的兩個標準:
熱點數據:被高頻訪問,如每秒幾十次以上。
靜態(tài)數據:很少變動,讀取要大于寫入。
以終端用戶為起點,系統所使用的數據庫為終點,這其中可以作為緩存設立點大致如下:
每個設立點都會擋掉一些流量,最終會形成以下的漏斗形效果,以此可以保護后面的系統以及最后的數據庫。
這些設立點就像是紅綠燈,如果沒有紅綠燈就容易發(fā)生事故,或是造成交通癱瘓等等。緩存設立點就是防止請求大量涌入,導致無法正常訪問。
四、緩存類別
上文已經羅列了緩存的幾種類別,接下我們將會對這些緩存類別進行介紹。
4.1 瀏覽器緩存
瀏覽器是離用戶最近的,可以用來作為緩存的地方,而且借助的是用戶的資源,性價比是幾種里面最好的,可以讓用戶分擔一些壓力。
進入瀏覽器的開發(fā)者工具,有 ?from cache
? 、?from memory cache
? 和? from disk cache
? 的時候,說明數據已經被緩存在用戶的終端設備上,在沒網的時候也可能訪問到一部分的內容就是這個原因。
瀏覽器會幫助我們完成這個過程,一般適用于圖片、 js 和 css等這些資源的緩存。
瀏覽器緩存的劣勢就是我們對它的掌控力比較差,沒有發(fā)起新的請求的情況下,是無法主動去更新數據。
4.2 CDN緩存
提供CDN服務的服務商,在全國乃至全世界部署了大量的服務節(jié)點。我們就可以將數據分發(fā)到遍布各地服務器上作為緩存,當用戶訪問時可以讀取就近的服務器上的緩存數據。這樣就可以分攤壓力和提升了加速效果。
要注意的是,由于節(jié)點眾多,更新緩存數據會比較慢,一般至少是分鐘級別,所以該緩存適用于不經常變動的靜態(tài)數據。
4.3 網關(代理)緩存
我們經常會在源站前面加上一層網管,目的是為了做一些安全機制或者作為同一分流策略的入口。
在這里設立一個緩存,能夠攔下來請求,其背后的源站也是收益很大的,減少了大量的 CPU 運算。常用的網關緩存有 ?Varnish
?、?Squid
?、?Nginx
?。
4.4 進程內緩存
一個請求能夠到這里來,說明是“業(yè)務相關”,需要經過業(yè)務邏輯的運算。從這里開始,對緩存的引入成本相對于前三者而言,要大大的增加了,這是因為對緩存與數據庫之間的‘數據一致性’要求更高了。
4.5 進程外緩存
這里是大多數程序員所熟悉的地方,就是 ?Redis
? 和? Memcached
? 之類,或者也可以自己單獨寫一個程序來轉存放緩存數據,提供給其他程序遠程調用。
4.6 數據庫緩存
數據庫緩存是數據庫的內部機制,一般都會給出設置緩存空間大小的配置來讓你進行干預。
最后,磁盤本身也是有緩存的,所以能夠讓數據平穩(wěn)地寫入到磁盤,可謂是經歷了一波三折。
五、緩存可能出現的問題
既然緩存作用如此大,那是不是就越多越好呢?只要速度慢就加緩存來解決?其實不然,緩存既有好的一面,也會有負面的一面。
5.1 緩存雪崩
問題:大量請求并發(fā)進入緩存時,可能由于某些原因緩沖效果未能正常執(zhí)行,即便是在很短的時間內,就會導致請求全部轉入數據庫,從而造成數據庫壓力過重。
解決:可以通過“加鎖排隊”或者“緩存時間增加隨機值”來解決此類問題。
5.2 緩存穿透
和緩存雪崩很相似,區(qū)別在于穿透會持續(xù)更長的時間。這是因為每次的? cache miss
? 后依然無法從數據源把數據加載到緩存,導致持續(xù)產生cache miss
?。
可以通過“布隆過濾器”或者“緩存空對象”來解決此類問題。
5.3 緩存并發(fā)
一個緩存 ?key
?下的數據被同時 ?set
?,怎么保證業(yè)務的準確性?如果進程內、進程外、數據庫三者的緩存一起用的情況下?
使用“先DB再緩存”的方式,并且緩存操作用 delete 而不是 set。
5.4 緩存無底洞
雖然分布式緩存是可以無線橫向擴展的,但是,集群下的節(jié)點也不是越多越好。緩存也是符合“邊際效用遞減”規(guī)律的。
5.5 緩存淘汰
內存的容量是有限的,如果請求的數據量是大量的,那么根據具體情況進行一定的淘汰策略是必不可少的。例如:?LRU
?、?LFU
?和?FIFO
?等等。
六、總結
本文主要介紹了Java緩存的運用方式的三種思路,Java緩存的類別,在系統中可以設立緩存的幾個位置,以及最后總結了 Java 緩存中會遇到的問題。如果想對文章中的具體細節(jié)有更多的了解,請關注 w3cschool 或 編程獅APP。