分布式緩存是指緩存部署在多個服務器組成的服務器集群中,以集群的方式提供緩存服務,其架構方式主要有兩種,一種是以JBoss Cache為代表的需要同步更新的分布式緩存,一種是以Memchached為代表的互不通信的分布式緩存。
1、JBoss Cache
Jboss Cache的分布式緩存在集群中的每一台服務器都緩存相同的數據,當集群中的某台服務器的緩存數據更新時,會通知集群中的其他服務器更新或者清除緩存。JBoss Cache通常將應用程序和緩存部署在同一台服務器上,應用程序可以從本地快速獲取緩存數據,但是這種方式帶來的問題就是緩存數據的數量受限於單一服務器的內存空間,而且當集群規模較大時,緩存更新信息需要通知集群中其他機器同步更新,這中間對於服務器和網絡帶寬來說,付出的代價是很驚人的。因而這種方案大多見於一般的企業級應用中,在大型網站中很少用。
2、Memchached
Memchached曾一度是網站分布式緩存的代名詞,被大量網站使用。其簡單的設計、優異的性能、互不通信的服務器集群、海量數據可伸縮的架構令網站架構師們趨之若鶩。
遠程通信設計需要考慮兩方面的要素,一是通信協議,即選擇TCP協議還是UDP協議,抑或是Http協議;一種是通信序列化協議,數據傳輸的兩端,必須使用彼此可識別的數據序列化方式才能使通信得以完成,如XML、Json等文本序列化協議,或者是Google的Protobuffer等二進制序列化協議。Memecached使用TCP協議(UDP也支持)通信,其序列化協議是一套基於文本的自動以協議,非常簡單,以一個命令關鍵字開頭,后面是一組命令操作數。例如讀取一個數據的命令協議是get<Key>。Memecached以后,很多NoSql產品都借鑒或直接使用了這套協議。
Memecached通信協議非常簡單,只要支持該協議的客戶端都可以和Memecached服務器通信,因此Memecached發展出了非常豐富的客戶端程序,幾乎支持所有主流網站的編程語言,因此在混合了多重編程語言的網站中,Memecached更是如魚得水。
Memcached服務端通信模塊式基於Libevent,一個支持事件觸發的網絡通信程序庫。Libevent的設計和實現有許多值得改善的地方,但他在穩定的長連接方面的表現卻正是Memecached所需要的。關於Libevent更詳細的內容,我們會在后面專門講Memecached的時候再詳細說明。
在上一篇文章中我們說到,緩存就是將數據存儲在訪問速度相對較高的存儲介質中,所以通產緩存都是存儲在內存當中。那么緩存數據都存儲在內存當中,必然會牽涉到一個問題,那就是內存的管理。而在內存管理中,令人最頭疼的問題就是內存的碎片管理。操作系統、虛擬機垃圾回收在這方面想了很多辦法:壓縮、復制等。Memecached使用了一個非常簡單的辦法,那就是固定的內存空間分配。
Memecached將內存空間分成一組slab,每個slab又包含一組chunk,同一個slab里面的每個chunk的大小是固定的,擁有相同大小chunk的slab被組織在一起叫做slab_class.
存儲數據時根據數據的Size大小,尋找一個大於Size的最小的chunk將數據寫入。這種內存管理的方式避免了內存碎片管理的問題,內存的分配和釋放都以chunk為單位的。和其他緩存一樣,memcached也是以LRU(最近最久未使用算法)算法釋放最近最久未被訪問的數據占有的空間,釋放的chunk被標記為未使用,等待下一個合適的數據寫入。
當然memecached的這種內存管理機制也會帶來內存浪費的問題,數據只能存在一個比它大的chunk中,而一個chunk只能存一個數據,其他空間就浪費了。如果啟動時參數配置不合理,浪費會更加驚人,發現沒有緩存多少數據,空間就沒了。
Memcached互不通信的特性是的Memecached從JBoss Cache、OSCache等眾多分布式緩存產品中脫穎而出,滿足網站對海量緩存數據的需求。其客戶端路由算法一致性Hash更成為數據存儲伸縮性架構設計的經典范式。事實上,正是集群中的分布式緩存服務器之間互不通信使得集群可以做到幾乎無限制的線性伸縮,這也正是目前流行的許多大數據技術的基本架構特點。
雖然近些年許多NoSql產品層出不窮,在數據持久化、支持復雜數據結構,甚至性能方面有許多都優於Memecached,但Memecached由於其簡單、穩定、專注的特點,仍然在分布式緩存領域占據重要地位。
對於Memecached相關技術知識,我們會在今后的文章中詳細的講解。