閱讀104 返回首頁    go 京東網上商城


Memcache Key 設計技巧及注意事項

剛剛開始研究Memcache,覺得Memcache Key的設計其實是十分重要的,搜了搜,感覺資料不是很多,下麵這些資料主要是從官網上獲得的,有些地方可能不太精確,僅供參考。

初始化Memcache Client

# perl
my $memclient = Cache::Memcached->new({ servers => [ '10.0.0.10:11211', '10.0.0.11:11211' ]});
# pseudocode
memcli = new Memcache
memcli:add_server('10.0.0.10:11211')
     某些客戶端的實現可能允許重複添加同一個Server,但最好每個Server是有一個實例,而且也要注意他們的順序最好不要經常變化,防止key的重新映射;在初始化MemcacheClient的時候要注意實例化的次數,不要太過頻繁的實例化,最好通過連接池來管理。

封裝一個SQL作為key

     Memcache在減輕數據庫負載方麵發揮著重要的作用,我們最常使用的場景也是用它來減少數據庫查詢次數,下麵這個例子就是通過Cache緩存查詢數據庫結果的場景,把sql+userId作為一個key,相同用戶第二次查詢時就可以從緩存中直接提取:
# Don't load little bobby tables
sql = "SELECT * FROM user WHERE user_id = ?"
key = 'SQL:' . user_id . ':' . md5sum(sql)
# We check if the value is 'defined', since '0' or 'FALSE' # can be 
# legitimate values!
if (defined result = memcli:get(key)) {
        return result
} else {
        handler = run_sql(sql, user_id)
        # Often what you get back when executing SQL is a special handler
        # object. You can't directly cache this. Stick to strings, arrays,
        # and hashes/dictionaries/tables
        rows_array = handler:turn_into_an_array
        # Cache it for five minutes
        memcli:set(key, rows_array, 5 * 60)
        return rows_array
}
     注意:在通過set方法緩存數據時,我們可以指定數據過期時間,上麵例子中設置的是5分鍾,這樣的話,五分鍾內用戶看到的都是同樣的信息,即5分鍾內的數據變化用戶是感覺不到的,在開發時一定要注意根據實際情況對過期時間進行合理設置。

封裝多個查詢SQL作為Key

     有些處理過程是複雜的,可能會用到多個sql,如果我們能把這些複雜的處理最後封裝成一個key,這是最理想的結果。比如下麵這個例子,我們把sql1+sql2+userId作為一個key,這樣,我們兩次查詢的結果便封裝成了一個CacheItem:
sql1 = "SELECT * FROM user WHERE user_id = ?"
sql2 = "SELECT * FROM user_preferences WHERE user_id = ?"
key  = 'SQL:' . user_id . ':' . md5sum(sql1 . sql2)
if (defined result = memcli:get(key)) {
        return result
} else {
        # Remember to add error handling, kids ;)
        handler = run_sql(sql1, user_id)
        t[info] = handler:turn_into_an_array
        handler = run_sql(sql2, user_id)
        t[pref] = handler:turn_into_an_array
        # Client will magically take this hash/table/dict/etc
        # and serialize it for us.
        memcli:set(key, t, 5 * 60)
        return t
}
     
緩存Object類型的數據    

     某些語言比如Java可以把對象進行序列化,進行序列化的對象就可以像普通字符串數據一樣進行緩存了,但要注意的是,序列化和反序列化會耗費cpu時間,在緩存時僅僅序列化需要緩存對象會提高係統效率。

緩存一個片段(網頁)

     Memcache不僅僅可以用來減少數據庫查詢,任何可以提高我們應用速度的地方我們都可以用它來解決,比如下麵這個例子,加載一個用戶模板是非常耗時的,但是我們可以把一個封裝好的模板(甚至是整個網頁)緩存起來:     

key = 'FRAG-BIO:' . user_id 
if (result = memcli:get(key)) {
        return result
} else {
        user         = fetch_user_info(user_id)
        bio_template = fetch_biotheme_for(user_id)
        bio_fragment = apply_template(bio_template, user)
        memcli:set(key, bio_fragment, 5 * 15)
        return bio_fragment
}

Get-By-Group-Key

      在某些情況下,我們希望一係列的key隻存儲到一台服務器緩存中,比如我們在顯示一個用戶首頁的時候,需要緩存他的姓名、年齡、簡曆、好友、日誌等信息,而通常情況下客戶端Hash算法會把它們的key映射到集群中的每一台機器的緩存中,如果這樣的話,我們在查詢一個人的主頁時會從多台機器上取數據,會耗掉很多網絡傳輸、Hash映射等時間,這是完全沒有必要的,我們需要一種機製,在客戶端內部把這些key組成一個group-key(可以是userId)。

其它需要注意的地方
  • 數據有效性:某些時候我們需要保證對Cache中的數據進行同步,不要讓用戶感覺到自己用到的是過期的髒數據。
  • 數據過期:我們可以設置緩存中的數據過期時間,最大過期時間是30天,如果參數值為0,則表示永不過期(LRU算法會自動刪除需要刪除的數據)。
  • 刪除Cache中數據:最直接的讓Cache數據過期的辦法就是delete。
  • Key的值越短越好:key的大小被限製在250個字節(將來可能達到6K),key的長度會影響Hash算法尋找value值的效率,也會浪費更多內存空間(期間有對key的copy)。

使用偽命名空間
     
     Memcache並不支持命名空間(其設計哲學就是簡單高效),但是我們可以模擬實現它。比如,我們可以把一個用戶的所有key都歸檔在一個命名空間下,比如可以把前綴設計為:user_namespace+userId。
user_prefix = memcli:get('user_namespace:' . user_id)
bio_data    = memcli:get(user_prefix . user_id . 'bio')
緩存Set 或 List

     存儲集合到緩存的意思是把一個集合的所有數據作為一個CacheItem保存到緩存中,當然,具體的實現方式有多種。不過需要注意一點的是,Memcache的每個Item有不能超過1MB的限製,那麼,我們該如何存儲一個超大數據量的集合呢?

方案一:分二個階段把數據緩存到Cache
     第一個階段存取的不是所有集合的數據,而是集合數據每一項的Id,然後第二階段再把每個Id對應的數據對象存到緩存中。當然,當一個集合特別大的時候,所有的數據的Id有可能也會超過1MB的限製,那麼我們在第一階段存儲時指定每個集合的數量即可。這麼做還有個好處就是當我們在更新一個數據條目的時候僅更新指定Id的項目即可。

方案二:一個階段分批次把數據存取到Cache
     比如有30000條數據,我們可以每批次100條分300批次緩存到Cache。








最後更新:2017-04-03 12:56:18

  上一篇:go 在Java裏處理文件的技巧
  下一篇:go 服務器證書安裝配置指南(Tomcat 6)