俗話說,計算機編程的任何問題,都可以通過增加一個抽象層來解決,這句話用在我身上就太合適了。
我是緩存(Cache),今天我給大家聊聊我這個抽象層是怎麼工作的。
提到我的名字,你可能立刻會想到到Redis, 因爲它實在是太普及了,但是如果你只想到Redis,那視野未必有點狹窄,Redis僅僅是我在應用層小試牛刀而已。
Wikipedia上說我是一種用來保存數據的硬件或者軟件,這樣以後的訪問請求就可以更快地返回。
這個定義還真是挺抽象的,抽象的東西讓人感覺不好理解,我得給大家舉幾個例子。
爲了突出我的位置, 在下面的圖片中,緩存都用藍色來表示。
首先來看大家日常使用很多,但是又不太在意的瀏覽器緩存。
瀏覽器緩存
瀏覽器面對的問題是網絡訪問的速度遠遠低於本地訪問的速度,每次都訪問網絡開銷太大。
於是它就請我增加了一箇中間層:開闢“緩存”區域,緩存JS, HTML, CSS,圖片等各種文件。
當然,瀏覽器這傢伙也不能亂來,得遵循一定的規則來判斷什麼時候用緩存中的文件,什麼時候不辭辛苦地去訪問服務器的新文件。
這其中的關鍵點就是HTTP協議:
在服務器發給瀏覽器的響應中,有expires, max-age, last-modified, Etag等Header, 粗略來說,expires 和 max-age 定義了一個資源的過期時間, last-modified和Etag用來檢查一個資源在服務器端有沒有變化。
對於它們的含義和詳細用法,網上資料多如牛毛,我這裏就不再展開了。
我覺得有趣的事情是這些:
|
|
|
CDN
說起CDN,可能很多人都意識不到它的存在,這也難怪,它對於大家來說幾乎是透明的,魔法發生在DNS的域名解析的過程。
但是CDN也是不折不扣的緩存。 由於網絡情況複雜,如果客戶端離服務器比較遠,網速慢,體驗會很差;海量的用戶給後端服務器帶來巨大壓力,所以CDN就採用了就近訪問的方案:
把後端服務器的數據數據複製多份,挪到離客戶端比較近的“邊緣”服務器中,就近訪問,不但減少了訪問的時間,還大大降低了 “中央”服務器的負載。
瀏覽器緩存和CDN是配合使用的, 瀏覽器的本地緩存失效以後,就需要向後端服務器來獲取了,但是如果一個系統有CDN,那瀏覽器還可以就近訪問CDN。
Linux Page Cache
在操作系統的世界中,時間是按納秒,微秒爲單位的,雖然內存和硬盤都在同一臺機器中,沒有網絡開銷,但是硬盤實在是太慢,比內存慢幾萬倍, 內存等不及。
所以Linux也增加了一個抽象層:Page cache , 把慢如蝸牛的硬盤的文件緩存在其中。
有了這個抽象層, 在Linux當中,幾乎所有的文件讀寫操作都依賴Page Cache,在向硬盤寫入文件的時候,並不是直接把文件內容寫入硬盤以後才返回的,而是寫入到內核的Page Cache就直接返回了。
這個Page Cache會被標記爲“Dirty”,隨後由內核線程寫入硬盤(也可以通過手工用sync命令寫入)。
各位看官可以想想,如果Page cache 的數據還沒有寫入硬盤,就斷電了,會發生什麼事情? 該怎麼處理?
當從硬盤讀取文件時,也不是直接把數據從硬盤複製到用戶態的內存,而是先複製到內核的Page Cache ,然後再複製到用戶態的內存。
正是由於這樣複製來複制去,在多個進程中間進行數據傳輸很麻煩,例如(一個進程讀取文件,然後通過Socket發送) ,所以後來就出現了零複製技術。
應用程序緩存
終於來到了大家熟悉的應用程序緩存, 這個就不用我多說了, 因爲數據庫訪問速度慢,無法應對大量的併發訪問,所以增加一個緩存中間層,把熱點數據從數據庫中取出,放到可以快速訪問的內存當中。
大名鼎鼎的Redis乾的就是這個活。
可是應用程序的緩存也是個雙刃劍,提升了訪問的效率, 但是帶來了很多複雜性:
|
|
|
|
CPU緩存
前面剛說到內存比硬盤快幾萬倍, 可是在CPU面前,內存也只能屈居下風,CPU比內存快100多倍,數據和指令必須從內存加載到CPU才能執行, 這次輪到CPU等不及了。
那就在CPU內增加緩存中間層,不過這次必須用硬件來實現。
CPU的緩存包括L1,L2, L3這三級Cache,把最熱點的數據和指令放入到其中。
我猜CPU Cache可能是最“底層”的Cache了。
在L1 Cache 最靠近CPU,速度最快,可以分爲指令Cache (CPU要執行的指令)和數據Cache(指令要操作的數據)。
CPU Cache 和上面提到的各種Cache比起來,小得可憐,也就是幾百K到幾M。 所以這些Cache要想發揮真正的作用,必須得依賴上帝的規矩局部性原理:
|
|
最後總結一下放置在我這裏的數據的特點,大家可以感受下:
|
|
|
|
如果你在工作中也遇到了問題,不妨考慮一下,看看能不能用我來解決問題:增加一箇中間層, 用空間來換取時間。