使用緩存的9大誤區(下)

作者: 汪洋  來源: InfoQ  發布時間: 2012-05-31 10:39  閱讀: 20696 次  推薦: 35   原文鏈接   [收藏]  

  相關文章:使用緩存的9大誤區(上)

  本篇文章在上篇的基礎上繼續討論了使用緩存的幾個誤區,包括:緩存大量的數據集合,而讀取其中一部分;緩存大量具有圖結構的對象導致內存浪費;緩存應用程序的配置信息;使用很多不同的鍵指向相同的緩存項;沒有及時的更新或者刪除再緩存中已經過期或者失效的數據。

  緩存大量的數據集合,而讀取其中一部分

  在很多時候,我們往往會緩存一個對象的集合,但是,我們在讀取的時候,只是每次讀取其中一部分。 我們舉個例子來說明這個問題(例子可能不是很恰當,但是足以說明問題)。

  在購物站點中,常見的操作就是查詢一些產品的信息,這個時候,如果用戶輸入了“25寸電視機”,然后查找相關的產品。這個時候,在后臺,我們可以查詢數據庫,找到幾百條這樣的數據,然后,我們將這幾百條數據作為一個緩存項緩存起來,代碼的代碼如下:

  同時,我們對找出的產品進行分頁的顯示,每次展示10條。其實在每次分頁的時候,我們都是根據緩存的鍵去獲取數據,然后選擇下一個10條數據,然后顯示。

  如果是使用本地內存緩存,那么這可能不是什么問題,如果是采用分布式緩存,問題就來了。下圖可以清楚的說明這個過程,如圖所示:

  相信大家看完這個圖,然后結合之前的講述應該很清楚了問題所在了:每次都按照緩存鍵獲取全部數據,然后在應用服務器那里反序列化全部數據,但是只是取其中10條。

  這里可以將數據集合再次拆分,分為例如25-0-10-products,25-11-20-products等的緩存項,如下圖所示:

  當然,查詢和緩存的方式有很多,拆分的方式也有很多,這里這是給出一些常見的問題!

  緩存大量具有圖結構的對象導致內存浪費

  為了更好的說明這個問題,我們首先看到下面的一個類結構圖,如圖:

  如果我們要把一些Customer數據緩存起來,這里就可以可能出現兩個問題:

  1. 由于使用.NET的默認序列化機制,或者沒有適當的加入相應Attribute(屬性),使得緩存了一些原本不需要緩存的數據。

  2. 將Customer緩存的時候,同時,為了更快的獲取Customer的Order信息,將Order信息緩存在了另外一個緩存項中,導致同一份數據被緩存兩次。

  下面,我們就分別來看看這兩個問題。

  首先看到第一個。如果我們使用分布式緩存來緩存一些Customer的信息的時候,如果我們沒有自己重新Customer的序列化機制,而是采用的默認的,那么序列化機制在序列化Customer的時候,會將Customer所引用的對象也序列化,然后在序列化被序列化對象中的其他引用對象,最后的結果就是:Customer被序列化,Customer的Order信息被序列化,Order引用的OrderItem被序列化,最后OrderItem引用的Product也會序列化。

  整個對象圖全部被序列化了,如果這種情況是我們想要的,那么沒有問題;如果不是的,那么,我們就浪費了很多的資源了,解決的方法有兩個:第一,自己實現序列化,自己完全控制哪些對象需要序列化,我們前面已經講過了;第二,如果使用默認的序列化機制,那么在不要需要序列化的對象上面加上[NonSerialized]標記。

  下面,我們看到第二個問題。這個問題主要是由于第一個問題引起的:原本在緩存Customer的時候,已經將Customer的其他信息,例如Order,Product已經緩存了。但是很多的技術人員不清楚這一點,然后又把Customer的Order信息去緩存在其他的緩存項,使用的使用就根據Customer的標識,例如ID去緩存中獲取Order信息,如下代碼所示:

  解決這個問題的方法也比較明顯,參看第一個問題的解決方案就可以了!

  緩存應用程序的配置信息

  因為緩存是有一套數據失效檢測周期的(之前說過,要么是固定時間失效,要么是相對時間失效),所以,很多的技術人員喜歡把一些動態變化的信息保存在緩存中,以充分利用緩存機制的這種特性,其中,緩存程序的配置信息就是其中一個例子。

  因為在應用的中的一些配置,可能會發生變化,最簡單的就是數據庫連接字符串了,如下代碼:

  當這樣設置之后,每隔一段時間緩存失效之后,就去重新讀取配置文件,這時候,可能此時的配置就和之前不一樣了,并且其他的地方都可以讀取緩存從而進行更新,特別是在多臺服務器上面部署同一個站點的時候,有時候,我們沒有及時的去修改每個服務器上面的站點的配置文件里面的信息,這個時候如何使用分布式緩存緩存配置信息,只要更新一個站點的配置文件,其他站點就全部修改了,技術人員皆大歡喜。OK,這確實看起來是個不錯的方法(在必要的時候可以采用一下),但是,不是所有的配置信息都要保持一樣的,而且還要考慮怎樣一個情況:如果緩存服務器出了問題,宕機了,那么我們所有使用這個配置信息的站點可能都會出問題。

  建議對于這些配置文件的信息,采用監控的機制,例如文件監控,每次文件發生變化,就重新加載配置信息。

  使用很多不同的鍵指向相同的緩存項

  我們有時候會遇到這樣的一個情況:我們把一個對象緩存起來,用一個鍵作為緩存鍵來獲取這個數據,之后,我們又通過一個索引作為緩存鍵來獲取這個數據,如下代碼所示:

  我們之所以這樣寫,主要因為我們會以多種方式來從緩存中讀取數據,例如在進行循環遍歷的時候,需要通過索引來獲取數據,例如index++等,而有些情況,我們可能需要通過其他的方式,例如,產品名來獲取產品的信息。

  如果遇到這樣的情況,那么就建議將這些多個鍵組合起來,形成如下的形式:

  另外一個常見的問題就是:相同的數據被緩存在不同的緩存項中,例如,如果用戶查詢尺寸為36寸的彩電,那么可能有可能一個編號為100的電視產品就在結果中,此時,我們將結果緩存。另外,用戶在查找一個生產廠家為TCL的電視,如果編號為100的電視產品又出現在結果中,我們把結果又緩存在另外一個緩存項中。這個時候,很顯然,出現了內存的浪費。

  對于這樣的情況,之前筆者采用的方法就是,在緩存中創建了一個索引列表,如圖所示:

  當然,這其中有很多的細節和問題需要解決,這里就不一一述說,要看各自的應用和情況而定! 也非常歡迎大家提供更好的方法。

  沒有及時的更新或者刪除再緩存中已經過期或者失效的數據

  這種情況應該是使用緩存最常見的問題,例如,如果我們現在獲取了一個Customer的所有沒有處理的訂單的信息,然后緩存起來,類似的代碼如下:

  之后,用戶的一個訂單被處理了,但是緩存還沒有更新,那么這個時候,緩存中的數據就已經有問題!當然,我這里只是列舉的最簡單的場景,大家可以聯想自己應用中的其他產品,很有可能會出現緩存中的數據和實際數據庫中的不一樣。

  現在很多的時候,我們已經容忍了這種短時間的不一致的情況。其實對于這種情況,沒有非常完美的解決方案,如果要做,倒是可以實現,例如每次修改或者刪除一個數據,就去遍歷緩存中的所有數據,然后進行操作,但是這樣往往得不償失。另外一個折中的方法就是,判斷數據的變化周期,然后盡可能的將緩存的時間變短一點。

  關于作者

  汪洋,現任惠普架構師、信息分析師《NET應用架構設計:模式、原則與實踐》作者。上海益思研發管理咨詢有限公司首席軟件架構專家,軟件咨詢組副組長。

35
1
 
標簽:緩存
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()