緩存的基本知識
在整個計算機體系構造中(無論是硬件層面還是軟件層面),緩存都是無處不在的。
在計算機硬件構造中,由於兩種介質的速度不匹配,高速介質在和低速介質交互時速度趨向低速方,這就導致了高速介質的資源閑置。而通過引入第三種介質(速度和成本介於兩者中間),將低速方讀寫的部分內容數據保存在該介質中,高速方大多數情況下則無需和低速方直接交互,這樣就能整體提升了交互的性能。這就是計算機體系中緩存的由來。比較典型的就是CPU緩存(CPU寄存器=>L1 cahce =>L2 cache =>內存=>硬盤),如圖:
在計算機系統和應用軟件層面,緩存更是無處不在。我們在使用瀏覽器上網時,很多靜態資源會被緩存到本地。我們在手機上采用微信聊天時,很多好友的頭像等數據會被緩存到手機中。在操作系統層面,I/O操作也會被內核緩存(一般將數據緩存在文件系統的緩存頁中),當然,這個可能相比前兩個場景更加抽象,但緩存的目的都是一致的,為了提升讀寫性能。
緩存在狹義上解決介質讀寫速度不匹配問題,廣義上包括任何利用中間媒介提高速度的方法,包括:空間換時間,動態操作變為靜態操作。
緩存(CACHE)和緩沖(BUFFER)
緩存:可以共享,多種數據,大小不固定,可以重復使用,已知數據,用於提高IO效率。
緩沖:不可以共享,單一數據,大小固定,讀取后失效,命中100%,未知數據,用於減少IO次數。
緩存的屬性
命中率:從緩存中返回正確數據的次數/總請求次數。
容量:超過這個值啟用一定的策略:轉移到磁盤;轉移到遠端;清空部分。
存儲介質:內存、磁盤。
成本:開發成本、部署成本、硬件成本。
效率:SET效率、GET效率、序列化、哈希算法、分布式算法。
緩存的限制
由於價格的因素,緩存實現依賴的存儲往往有大小限制——保存什么,舍棄什么,命中率。
緩存往往是從無到有的——在最初階段不能發揮作用,在不命中的時候性能顛簸。
緩存的分類
按照存儲介質來分 :
內存(網站進程內、同服務器獨立進程、獨立服務器、分布式服務器組)。
磁盤(本地文件和數據庫,獨立服務器、分布式服務器組)。
緩存可以使用磁盤而不僅僅是內存。
按照存儲的數據來分 :
直接用於輸出的整頁(HTML、腳本樣式、圖片)。
片段頁(可供多個客戶端使用的HTML、腳本樣式等)。
索引和聚合數據(空間換時間)。
耗時查詢的結果數據。
和業務相關的大塊數據(列表數據,引用數據)。
和業務相關的小級數據(行級數據,資源數據)。
和上下文(用戶)相關的數據(活動數據)。
按照實現方式來分 :
框架或引擎內置的緩存(比如ORM緩存和SQL SERVER緩存)。
安裝特定的組件根據規則自動實現緩存(比如反向代理和輸出緩存)。
需要由開發以編程方式實現的緩存(比如業務數據緩存)。
按照作用來分 :
用於數據的讀取(之后介紹的大部分內容都是基於此類緩存)
用於(允許丟失)數據的寫入——寫到緩存的隊列中,再由工作線程提交處理(寫入存儲)
網站架構中的緩存
瀏覽器緩存(HTTP緩存頭)
代理緩存(Squid Vanish CDN)
Web服務器緩存(內核緩存、應用緩存)
頁面輸出緩存(片段緩存、整頁緩存)
業務數據緩存(本地緩存,分布式緩存)
其它緩存(ORM、數據庫、搜索引擎等緩存)
這里每一塊涉及的技術和內容都比較多,暫時不展開。
緩存的常見模式和策略(過期、更新、清除)
緩存的常見模式:
緩存的策略:
被動更新
在獲取數據的時候,如果緩存中沒有(可能緩存已經過期了),則嘗試去數據庫中獲取,最后將最新的數據再寫入到緩存中。
主動更新
更新數據的時候,主動更新緩存(直接將數據寫入到緩存中)。
定時更新
通過獨立的線程或者任務調度,定時將緩存數據進行更新。
通知更新
可以通過MQ等方式來通知緩存更新,這其實也是一種主動更新的策略,一定程度可以解耦。
不更新
對於有些不可能發生改變的數據,可以永久緩存起來。
緩存的過期(失效)策略:
絕對的過期時間
這種是最常見的方式。當到達指定時間后,緩存將自動失效。
平滑過期
類似session的超時機制,實際使用較少。
依賴方式
在.NET平台內置的本地緩存中,支持依賴數據庫或文件來更新緩存,但實際開發中很少用。
永遠不過期
對於有些不可能發生改變的數據,可以永久緩存起來。
緩存的清除(替換)策略:
RAND 刪除隨機數據,不能反映局部性。
SIZE 刪除最大的數據。
FIFO,First In First Out 刪除最先進入緩存的數據,不能反映局部性。
LFU,Least Frequently Used 刪除一直以來最少被使用的數據。
LRU,Least Recently Used 刪除最近最少使用的數據。
其中,LUR算法是最被廣泛使用的。
常見模式:
延遲加載方式:A+F
預加載方式:B/C/E+I
下一篇跟大家聊聊緩存命中率、緩存的常見問題和應對方式...