最近公司一直在招人,我作為主考官之一 。經常會提問的一個問題,就是讓使用者介紹自己在緩存方面的經驗和心得。絕大多數的面試者隻能說 asp.net 的頁面緩存和局部緩存,稍稍有點經驗的,會提到企業庫的緩存,隻有很少的人會知道 memcached(一個分布式的緩存)。而對于緩存的一些基本思想,卻沒有一個人能說出來。
現在的技術人員,很多時候,不管三七二十一,把一個個實體丢到緩存中,然後在用的時候,就從緩存中去找這個實體。而不會考慮緩存的其他方面因素。是以他們在提到緩存時,想到的才隻能是一個個的緩存實作的方法,而不是緩存的思想。
那麼,肯定就有人問,蝈蝈俊,那你了解的緩存思想是如何的呢?下面我就一一來說出我了解的緩存。
能解決問題的緩存就是好緩存。這句話簡直就是廢話,相當于白貓、黑貓,抓住老鼠的就是好貓。
那在解決問題前提下,哪個緩存才是好緩存呢? 這個問題我的答案是:緩存命中率高的緩存是好緩存。
在解決問題前提下,命中率高的緩存比命中率低的緩存,在硬體投入上可能會比較小,同時緩存的數量比命中率低的緩存數量也可能少,這樣尋址的速度肯定比較快。是以命中率高的緩存是好緩存。
一個緩存的實體在被丢到緩存中後,在這個實體被緩存的期間(這個實體被緩存的生命周期内),如果外部一次都沒有使用過它,這個緩存實體的命中率就是0。這個實體被請求的次數越多,它的緩存命中率越高。
上面說的是緩存中一個實體的命中率。對于緩存整體來說,它的命中率則是上面各個被緩存的個體的命中率分布圖。
對于緩存來說:通常最常使用的個體之占總體的很小一部分。最不常使用的占整體的很大一部分。
是以我們經常會看到類似這樣的資料:
緩存的1萬個元素中,有100個被頻繁的使用,幾乎每分鐘都會被使用一次。2000個資料,每小時被請求一次。3000個資料,每天被請求一次,剩下的資料,被丢到緩存中後一次都沒有被使用過。
但是:硬體發展快,資料量發展也快。小型的網站,緩存1萬條資料,也就全部緩存了。但是大型網站最少也是上百萬的資料量或者上t級别的資料,這些資料量顯然不能都丢到緩存。這時候設計一個合理的緩存方案,提高緩存的命中率,就非常重要。而且是必須的。
純技術的角度來說,我們隻有記錄了使用者的機關時間的請求數,并依照這個資訊來把最常被使用的資料緩存起來。
但更多的時候,我們是根據業務邏輯來提高緩存命中率的。比如:去年,前年發表的部落格,這類文章的浏覽請求,一般一天至少可憐的幾次。一般不應該緩存到記憶體中。
又比如,回複數多的文章,一般被請求數會比回複數少的文章會被更多人次看到。
一個反面的例子就是:不管三七二十一,一個大型的部落格站點,一篇文章被使用者請求的時候,發現不在記憶體緩存中,就從資料庫中讀出,然後丢到緩存。
要知道,現在爬蟲程式很多的。另外,部落格這類搜尋引擎友好的站點,決大多數的通路壓力是搜尋引擎搜尋過來的。而這些通路一般都是1小時,或者1天之内,對某篇文章隻有幾次甚至1次請求,之後再也沒有了。上面作緩存的方法,命中率會非常低的。
這裡也許就有人會問,郭紅俊,既然你不建議我緩存這些部落格的内容,但是我如何提高我站點的性能呀,我至少得保證我部落格站點不會速度慢的無法響應使用者請求呀。
這個問題的解決方案有很多,一個最簡單的方法就是把這些部落格做成靜态html頁面,也就是檔案系統的緩存,檔案系統因為硬碟的原因,可以簡單了解成可以無限擴容,這樣就可以把很多命中率低的内容進行緩存。
如果你的頁面需要一些動态邏輯判斷,你可以把資料緩存成xml檔案,然後伺服器段整合這些xml檔案,或者是包含檔案。這也是種不錯的方法。
說了這麼多緩存命中率的問題,簡單彙總一下緩存命中率的觀點:
小型網站可以全部資料緩存,一般壓力也不會很大,可以忽略緩存命中率問題。
大型服務無法全部資料緩存,隻能部分資料緩存,這時候就需要架構師設計出對該業務邏輯适用的緩存方法,盡可能的提高緩存的命中率。
提高命中率的方法大多是跟業務邏輯捆綁的,需要跟具體問題具體分析
對于不能被記憶體緩存的資料,最簡單的提高性能方法就是使用檔案緩存。
檔案緩存可以整個内容緩存成一個靜态檔案;也可以是整個頁面的一個區域被緩存成一個檔案,然後被包含;也可以是把一個實體序列化成xml檔案進行緩存。
下面我們看看緩存的其他幾個不那麼重要的方面:
永久不過期,永久不變更的内容,這類東西就不應該放在緩存。緩存是臨時的存儲,而不是永久的,是以緩存的生命周期是有限的。
它依次可能會經曆如下活動:
進入緩存。(進入緩存的時候,可能需要指定它以後的過期政策,如果不指定,需要使用系統預設的過期政策)
從緩存中獲得它,注意,這時候需要處理線程安全的問題。
更新緩存,注意,也需要考慮線程安全問題
離開緩存,這個可能是外部請求,也可能是緩存根據過期政策把它清理掉。
一般我會問,你所接觸的緩存中,碰到過那些緩存過期政策?
最常見的幾種過期政策如下:
多長時間沒有被請求,則過期,最典型的就是asp和asp.net 提供的 section 功能。其實它就是一個緩存。
依賴于檔案變更的緩存,一旦檔案被修改,緩存則過期,典型的是 web站點的 web.config ,一旦這個檔案變更,不但緩存重起,iis程序也會進行一次釋放工作。
在此基礎上,可能看到很多依賴關系的緩存過期政策。比如依賴于資料庫的緩存過期政策。
當然,業務邏輯裡可能會有更複雜的過期政策,必須csdn新版積分制論壇中,文章清單緩存會在清單資料緩存達到600時,把它清理到550條資料。
又比如新積分制論壇文章的緩存過期,則是沒有任何清單引用這個文章後,則這個文章過期。
使用緩存,則意味着同樣的資料,可能有多份并存。如果你的代碼沒有考慮某種情況,導緻了這兩份資料不一緻了。這時候就會有問題發生。
解決方法很簡單,把你的業務邏輯,代碼觸發情況都考慮清楚,不要遺留沒有觸底的地方。
簡單的方法會導緻你的代碼邏輯變得非常複雜。
這也就是有些人,在非必要的時候,建議你不要用緩存的原因。一旦開始使用緩存,你就應該準備增加大量的代碼來處理資料同步的問題。
有時候在緩存被初始化後,還需要預先填充一些資料到緩存中。這就是緩存資料的初始化操作。
緩存資料的初始化操作需要考慮以下問題:
需要多長時間進行初始化,一般如果是站點的話,我們可能在 global.asa 的 application_onstart 中處理這個初始化工作。初始化的一般不能太久,這時候就是考驗我們代碼優化的能力了。
初始化的時候,一般是批量導入資料,而不是我們正常使用的時候,一次處理一個資料。
本文介紹了我對緩存的一些觀點,而沒有深入涉及到具體的緩存技術。希望通過本文的講述,讓隻會緩存用法不懂緩存思想的人有初步的了解。