主要內容:
Memcached基本的工作原理
Memcached的兩階段哈希
Memcached的數據存儲方式
Memcached新建Item分配內存過程
Memcached的數據過期方式
Memcached與Spring集成
------------------------------------------------------------------------------------------
Memcached基本的工作原理
Memcached是以守候程序的方式運行於一個或者多個服務器中,隨時等待客戶端的鏈接請求,通過啟動Memcached服務端,配置相應的監聽IP、端口等參數,客戶端可通過指定的服務器IP 將數據以key-value的方式存儲到Memcached實例中。
如下圖所示,有N個Memcached實例部署在一台專門的機器上,應用程序在查詢數據時首先去Memcached里面查詢,如果有數據則直接返回客戶端使用,如果Memcached里面沒有需要的數據或者數據過期了,則應用程序先去查詢數據庫,然后把查詢的結果放到Memcached實例里面去,最后返回給客戶端。
Memcached的兩階段哈希
第一階段哈希是計算"key-value"存儲在哪個Memcached實例上,第二階段哈希是計算"key-value"是存儲存儲在Memcached實例的哪個chunk里面。
客戶端存取數據時,首先參考Memcached實例列表計算出key的哈希值(階段一哈希),進而選中一個Memcached實例;客戶端將請求發送給選中的Memcached實例節點,然后該Memcached節點通過內部的哈希算法(階段二哈希) 進行真正的數據(Item)存取。 Memcached的服務器和客戶端通信並不使用復雜的XML等格式,而是使用簡單的基於文本行的協議。
如下圖所示:
比如我們要在Memcached上存儲 key為"Hello",value為"World" 的這么一個鍵值對。
階段一哈希: 首先在客戶端根據key計算出該"鍵值對"會存儲在哪個Memcached實例中,假如是m2
階段二哈希: 在Memcached實例二中 再次根據key計算出該"鍵值對"在m2實例中存在的位置。
Memcached的數據存儲方式
Memcached的數據存儲方式被稱為Slab Allocator,其基本方式是:
(a)先把內存分成很多個Slab,這個大小是預先規定好的,以解決內存碎片問題。
分配給Slab的內存空間被稱為Page,默認是1M。一個Slab下可以有多個Page。
(如下圖:在Memcached實例中先把內存分了n個slab,然后每個slab里面分了n個page)
(b)然后把一個Page分成很多個chunk塊,chunk塊是用於緩存記錄的空間。Chunk的大小是先有一個基本值,然后根據增長因子來增大。
Slab Allocation的原理——將分配的內存分割成各種尺寸的塊(chunk), 並把尺寸相同的塊分成組(chunk的集合),每個chunk集合被稱為slab。
(借用網上一張圖來說明:)
(c)slab class:內存區類別(48byte-1M),每個類別有一個slab classId
(d)Memcached里面保存着slab內存空間的chunk列表,當收到要保存的item時,它會根據item的大小去選擇一個最適合的slab,然后找到空閑的chunk,把數據存放進去。
Memcached新建Item分配內存過程
第一步: 快速定位slab classid,先計算Item長度
key鍵長+flag+suffix(16字節)+value值長+結構大小(32字節),比如計算出來有90byte
如果Item長度計算出來>1M,則無法存儲丟棄
取最小冗余的slab class進行存儲,比如有:48、96、120, 存儲90就會選96
第二步:按順序尋找可用的chunk
(a) slot : 檢查slab回收空間slot里是否有剩余chunk
delete: delete時標記到slot
exptime: get時檢查的過期對象標記到slot
(b)end_page_ptr: 檢查page中是否有剩余chunk
(c)memory: 內存還有剩余空間可以用於開辟新的slab
(d)LRU
(PS:Memcached的數據存儲方式的缺點,由於chunk的大小是預先分配好的特定長度,因此如果數據不能完全填滿chunk,那么chunk中剩余的空間就浪費了。)
Memcached的數據過期方式
(1)Lazy Expiration(延遲/惰性 過期)
Memcached不會監控記錄是否過期,而是當客戶端來獲取數據的時候,才會檢查記錄的時間戳,因此成為Lazy Expiration
(2)LRU(Least Recently Used 最近最少使用)
當空間不足的時候,Memcached會優先使用已經過期的數據空間,如果還不夠的話,那么就會把最近最少使用的對象的空間釋放出來。
(ps:要特別注意,Memcached的LRU不是全局的,而是針對slab的,可以說是區域性的)
(3)惰性刪除機制
刪除item對象時,不釋放內存,作刪除標記,指針放入slot回收插槽,下次分配的時候直接使用。
Memcached與Spring集成
(1)先把jar包添加到本地倉庫中:
mvn install:install-file -Dfile=java_memcached-release_2.6.6.jar -DgroupId=com.danga -DartifactId=memcached -Dversion=2.6.6 -Dpackaging=jar -DgeneratePom=true
(ps:注意 -Dfile=java_memcached-release_2.6.6.jar 一定要輸入jar包的全路徑名 )
(2)在pom中添加memcached的lib依賴和common-pool的lib依賴:
1 <dependency> 2 <groupId>commons-pool</groupId> 3 <artifactId>commons-pool</artifactId> 4 <version>1.5.6</version> 5 </dependency> 6 <dependency> 7 <groupId>com.danga</groupId> 8 <artifactId>memcached</artifactId> 9 <version>2.6.6</version> 10 </dependency>
(3)在spring的applicationContext.xml文件中加入memcached的配置
1 <bean id="memcachedPool" class="com.danga.MemCached.SockIOPool" factory-method="getInstance" 2 init-method="initialize" destroy-method="shutDown"> 3 <constructor-arg> 4 <value>neeaMemcachedPool</value> 5 </constructor-arg> 6 <property name="servers"> 7 <list> 8 <value>192.168.1.81:3333</value> 9 </list> 10 </property> 11 <property name="weights"> 12 <list> 13 <value>1</value> 14 </list> 15 </property> 16 <property name="initConn"> 17 <value>5</value> 18 </property> 19 <property name="minConn"> 20 <value>5</value> 21 </property> 22 <property name="maxConn"> 23 <value>5</value> 24 </property> 25 <property name="maintSleep"> 26 <value>30</value> 27 </property> 28 <property name="nagle"> 29 <value>false</value> 30 </property> 31 <property name="maxIdle"> 32 <value>6000</value> 33 </property> 34 <property name="socketTO"> 35 <value>3000</value> 36 </property> 37 </bean> 38 39 <bean id="memCachedClient" class="com.danga.MemCached.MemCachedClient"> 40 <constructor-arg> 41 <value>neeaMemcachedPool</value> 42 </constructor-arg> 43 </bean>
在程序中就可以通過 MemCachedClient的get、set方法來往Memcached里面獲取和設置item了