1. 程式人生 > >Memcache(一)服務端

Memcache(一)服務端

目錄

memcache簡介     memcache是目前主流的一個高效能的分散式記憶體物件快取系統,它以key-value形式在記憶體中儲存資料,由於資料快取在記憶體中,所以相比操作DB而言,它不需要解析SQL、磁碟I/O等開銷,效率更高。memcache通常用於在動態Web應用程式中減輕資料庫負載,提升系統性能。也經常作為伺服器之間資料共享的儲存媒介,比如儲存分散式session。理論上來講,memcache儲存資料的量只取決於伺服器記憶體的大小。         作為一個高效能的分散式快取系統,討論單一快取節點並沒有太大的意義。首先隨著系統使用者的增長或業務的擴充套件等,資料量與訪問量會越來越多,單一節點可儲存的資料量不能滿足需求

,其次單一節點的0容錯率意味著宕機後併發壓力將直接再次轉移到DB,很可能導致RDBMS的負擔加重、資料庫響應惡化、 網站顯示延遲等重大影響。所以不管是從效能上還是容錯率上來看,高併發情況下,單一快取節點並沒有太大的意義,必須搭建一個快取伺服器叢集環境。      但memcache在設計時,其Server之間無法通過廣播或其它方式同步快取資料,這麼設計的好處雖然從空間和時間上都節約了成本(1理論上總體可儲存的資料量M*N [記憶體大小*伺服器臺數],從空間利用率上來說達到了最大化; 2從時間成本上來說,由於不同步資料,所以不會隨著伺服器數量的增加,或儲存數量的增多而增加通訊時間成本 )。      但缺點也明顯,首先需要客戶端來保證存取伺服器一致
;其次由於資料不同步,所以某個節點Down掉後,對它的所有獲取操作都將重新轉移到DB(不考慮其它儲存方式),這種情況下必須儘量降低單個節點宕機的影響,防止資料庫負載過高。       所以整體上Memcache可以看成兩部分:客戶端和服務端。其中由客戶端決定如何選擇儲存或檢索的伺服器,以及在無法聯絡伺服器時(比如宕機或網路問題)要執行的操作;而服務端則負責資料的儲存和檢索,及記憶體的釋放或重用。

  • 客戶端,提供可用的memcached伺服器列表及路由演算法等。
  • 伺服器,儲存及確定何時丟棄舊資料(如果記憶體不足)或重用記憶體,比如LRU。

分散式環境下互動的總體流程大致可以用下圖表示memcache工作原理 (1)memcache記憶體管理記憶體結構

        memcache會在記憶體中開闢一塊空間,建立一個統一的巨大的hash表用來儲存各種格式的資料,包括影象、視訊、檔案以及資料庫檢索的結果等。存放資料時,首先slab要申請記憶體,申請記憶體是以page為單位的,申請到page後,slab再將page按chunk的大小進行切分變成一個chunk陣列,最後從這個chunk陣列中選擇一個用於儲存資料。所以整體上記憶體儲存結構如下 1.slab_class裡,存放的是一組組chunk大小相同的slab 2.每個slab裡面包含若干個page,page預設大小為1M 3.chunk是資料的實際存放單位,若干個chunk組成一個page。記憶體分配         memcached預設情況下采用Slab Allocator的機制分配、管理記憶體。在該機制出現以前,記憶體的分配是通過對所有記錄簡單地進行malloc和free來進行的,這種方式很容易產生記憶體碎片,降低作業系統記憶體管理效率,甚至會導致作業系統比memcached程序本身還慢。而Slab Allocator的基本原理是按照預先規定的大小,將分配的記憶體分割成特定長度的塊,再把尺寸相同的記憶體塊分成組,存放資料時,根據資料大小去匹配slab大小,找就近的slab存放。由於chunk大小固定,所以Slab Allocator機制雖然解決了記憶體碎片問題,但卻產生了記憶體浪費的情況。         比如在memcached -p 11212 -m 128m -vv -u root -f 10分配情況下(-m引數用於指定分配記憶體大小;-f 表示增長因子,在某種程度上控制slab之間的差異,早前版本中固定值2,現在預設值為1.25)。 如果儲存資料總共是50個位元組,它將進入slab class 1,損失46個位元組。如果資料總共100個位元組,將存入slab class 2,損失860個位元組。(這裡也看出增長因子的合理設定很重用,它將直接影響記憶體的使用率)

記憶體回收方式      Lazy Expiration       在1.5.0版本之前,memcached內部不會監視記錄是否過期,而是在get時檢視記錄的時間戳,檢查記錄是否過期。這種技術被稱為lazy(惰性)expiration。因此,memcached不會在過期監視上耗費CPU時間,這樣可以減輕伺服器的負載。從官方描述中看,在1.5.0版本之後,memcache提供了自動監控並清理的機制,這類似於Java的垃圾回收機制一樣,雖然從時間成本上來說會消耗一定的CPU時間,但卻能適時的釋放空間,提高記憶體使用率。

LRU演算法         當memcached的記憶體空間不足時(無法從slab class 獲取到新的空間時),memcache採用LRU演算法根據設定的失效時間清理失效的快取資料,或者從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。從快取的實用角度來看,該模型十分理想。需要注意的是,如果禁用LRU(memcache啟動時沒有指定-M)的情況下記憶體不夠會報出Out Of Memory錯誤。 (注:小寫-m表示分配記憶體大小,預設64M。大寫-M表示啟用LRU演算法)

(2)memcache分散式分散式實現原理             分散式實現原理在上文其實已經說過了,目前memcache多個Server之間互不通訊,各自儲存的資料也不同,每個Server只對自己管理資料負責。所以memcache分散式是通過客戶端實現的,採用了單程序、單執行緒、非同步I/O,基於事件(event_based)的服務方式.使用libevent作為事件通知實現。         Client端通過IP地址和埠號指定Server端,將需要快取的資料是以key->value對的形式儲存在Server端。key的值通過hash進行轉換,根據hash值把value傳遞到對應的具體的某個Server上。當需要獲取物件資料時,也根據key進行。首先對key進行hash,通過獲得的值可以確定它被儲存在了哪臺Server上,然後再向該Server發出請求。Client端只需要知道儲存hash(key)的值在哪臺伺服器上就可以保證存取伺服器一致分散式路由演算法餘數演算法:用鍵的雜湊值%伺服器臺數,根據餘數確定存取伺服器,這種方法計算簡單高效,但在memcached伺服器增加或減少時,幾乎所有的快取都無法命中,這是餘數演算法一個致命問題。這裡我用不同埠代表不同Server進行了一個簡單的測試,開啟了11211 11311 11411三臺Server,對應如下

Java測試程式碼如下

//獲取伺服器配置,並註冊
		CacheConfig cfg = new CacheConfig();
		cfg.setServerAddress("192.168.80.128:11211,192.168.80.128:11311,192.168.80.128:11411");
		TvlCache cache = TvlCacheFactory.getCache(cfg);
		//存
		cache.add("0", "0");	//48%3=0,對應11211埠伺服器
		cache.add("1", "1");	//對應11311
		cache.add("2", "2");	//對應11411
		
		//休眠3s,停掉11411
		Thread.sleep(3000);
		
		//取
		System.out.println(cache.get("2"));	//50%2=0,對應到11211埠伺服器,print:null
		

不停任何節點的情況下,key"2"存取Server一致,對應11411Server。如果在Thread.sleep(3000)期間停掉11411,對應於下

Consistent Hashing雜湊演算法:首先求出memcached伺服器(節點)的雜湊值,並將其配置到0~2{^{32}}的圓(continuum)上。然後用同樣的方法求出儲存資料的鍵的雜湊值,並對映到圓上。然後從資料對映到的位置開始順時針查詢,將資料儲存到找到的第一個伺服器上。如果超過2^{32}仍然找不到伺服器,就會儲存到第一臺memcached伺服器上。 如果需要從上圖的狀態中新增一臺memcached伺服器。採用餘數演算法會由於伺服器數量的變化而對快取命中率產生巨大影響,但Consistent Hashing中,只有在continuum上增加伺服器的地點逆時針方向的第一臺伺服器上的鍵會受到影響。

memcache總結

  1. 理論上來說memcache中可以儲存的item資料量是沒有限制的,只要記憶體足夠
  2. memcache單程序在32位機中最大使用記憶體為2G,64位機則沒有限制
  3. Key最大為250個位元組,超過該長度無法儲存
  4. 由於記憶體申請是以page為單位,而page大小預設為1M,所以單個item最大資料是1MB,超過1MB的資料不予儲存(如果需要儲存大集合等資料,建議拆分儲存但記憶儲存的關係
  5. memcache服務端是不安全的,比如已知某個memcache節點,可以直接telnet過去,並通過flush_all讓已經存在的鍵值對立即失效,所以memcache伺服器最好配置到內網環境,通過防火牆制定可訪問客戶端,,從物理上進行隔離
  6. 不能夠遍歷memcache中所有的item,因為這個操作的速度相對緩慢且會阻塞其他的操作
  7. memcache的高效能源自於兩階段雜湊結構:第一階段在客戶端,通過Hash演算法根據Key值算出一個節點;第二階段在服務端,通過一個內部的Hash演算法,查詢真正的item並返回給客戶端。從實現的角度看,MemCache是一個非阻塞的、基於事件的伺服器程式
  8. memcache設定新增某一個Key值的時候,傳入expiry為0表示這個Key值永久有效,這個Key值也會在30天之後失效。(版本原始碼)