1. 程式人生 > >由Memcached使用不當而引發效能問題的兩個經驗總結

由Memcached使用不當而引發效能問題的兩個經驗總結

在這個cache everywhere的時代,在這個人人都會說分散式快取的時代,Memcached幾乎已成為網站開發中的標配。

作為一名普通的coder,我們在編寫快取程式碼的時候,很多情況下可能都只是瞭解其基本原理,知道如何呼叫API,知道大概怎麼work around,然後測試通過上線,通常這樣做還真不會出事。

然而看到這幾天評論猛烈的雄文因為所謂的程式碼效能不高而被離職的程式設計師及其回帖,以及之前公司內部培訓發現竟然有很多人不知道framework的快取是天生的Thread Safe,實在忍不住,拋開非技術話題,也不討論程式碼可讀性,就說說Memcached快取的使用,用好用對其實並不容易,而且說不定就會有隱藏問題,真的太有總結的必要了。

1、key-value的限制

快取的key有長度限制,key的組成有特定字元的限制。

快取的value必須可以序列化,且快取的單一value容量有大小限制,對於可序列化的value,應該想方設法儘量規避某些特定資料結構,比如Hashtable,DataTable這些內部其實非常非常之複雜的資料結構。對於讀頻繁的操作來說,每次序列化和反序列化複雜資料結構的開銷可想而知。

如果連分散式快取的key和value(尤其是value)的一般限制都搞錯了,那麼使用快取的後果很可能只是白白增加了網路IO及序列化、反序列化的開銷,對系統性能提升當然是巨大的反作用。

2、小心Memcached的.net客戶端的誤用

這一點隱藏的也比較深,下面以應用廣泛的EnyimMemcached為例來簡單說明。

通常我們使用的客戶端每次例項化MemcachedClient物件內部都會初始化一個客戶端物件池(TCP連線池,客戶端命名為ServerPool)。所謂TCP連線池就是將建立好的TCP連線(連線數通常按照配置來,生產環境的配置不會小於兩位數)初始化放在容器內,客戶端呼叫的時候可以直接拿出已經存在的TCP連線來用,這樣可以省去實時開啟TCP連線的開銷。

因為有人喜歡using一下(當然包括樓主自己了),一看到MemcachedClient是繼承自IDisposable的,必須用using啊,然後就要new一個MemcachedClient物件,這樣客戶端內部也就不得不再初始化一個TCP連線池。如果某個使用快取的服務方法呼叫頻繁,很快你就會發現系統CPU飆升,頁面開啟速度奇慢,直至不能正常訪問。

我們知道,分散式快取系統都有一個TCP連線上限的設定,無論如何,超過這個上限都有可能引發連環反應,這種反應毫無疑問是不良副作用。

所以如果我們誤用MemcachedClient,每次都new一個物件,那麼高併發情況下效果就非常慘了,一方面web伺服器因為TCP連線過多無法正常訪問,另一方面Memcached伺服器也因為連線太多負載過重而效能變得極差,依賴Memcached的服務很可能短時間內只接收到超時響應。

解決方案無比簡單,配置合適的TCP連線數,MemcachedClient物件單例即可。

最後還要重申選擇使用快取的業務場景的重要性。這一點真的無法說透,但是根據一些已有經驗,可以提煉出兩條比較通用的快取準則:

1、寫頻繁的資料不適合快取;

2、讀頻繁而寫不頻繁的資料適合快取。

果然正確的話都是廢話,上面兩條等於沒說。

怕你們說無聊,還是要奉獻兩條自己使用快取的主要準則,當然只是自己一家之言經驗之談,不可全信,切記,否則被總監勸退老子概不負責:

1、適合快取的資料通常應該對外公開供(所有)人呼叫,私有的資料快取多數情況下是沒有意義的;

2、對準確性、實時性、安全性等要求極高的業務資料,你的資料可能不適合快取。

順帶再提一下web開發中的效能問題。據說如果一個網站有效能問題,那麼它一定會出現效能問題。資料庫、快取、訊息佇列、各種框架、com元件等等等等,這些web開發中的標配,有哪個使用不當不會引發系統的效能問題呢?甚至大家習以為常的拼接字串在特定條件下都會造成系統崩潰。我們也知道生產環境就像國際政治一樣錯綜複雜波譎雲詭,測試環境、UAT環境通過並不能保證系統諸事無虞,應該時刻認識到coding無小事,否則一個疏忽就有可能造成生產環境發生悲劇乃至慘劇。

參見:http://www.cnblogs.com/jeffwongishandsome/archive/2013/09/21/3331733.html