﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-昊天-随笔分类-memcached</title><link>http://www.blogjava.net/hao446tian/category/51361.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 29 Jan 2013 10:21:48 GMT</lastBuildDate><pubDate>Tue, 29 Jan 2013 10:21:48 GMT</pubDate><ttl>60</ttl><item><title>memcached的总结和分布式一致性hash</title><link>http://www.blogjava.net/hao446tian/archive/2013/01/29/394858.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Tue, 29 Jan 2013 03:26:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2013/01/29/394858.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/394858.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2013/01/29/394858.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/394858.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/394858.html</trackback:ping><description><![CDATA[<p>当前很多大型的web系统为了减轻数据库服务器负载，会采用memchached作为缓存系统以提高响应速度。</p>
<p>目录：</p>
<ol sizcache="0" sizset="25"><li sizcache="0" sizset="25"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#intro">memchached简介</a></li><li sizcache="0" sizset="26"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#hash">hash</a> 
<ul sizcache="0" sizset="27"><li sizcache="0" sizset="27"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#mode">取模</a></li><li sizcache="0" sizset="28"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#consist">一致性hash</a></li><li sizcache="0" sizset="29"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#virtual">虚拟节点</a></li><li sizcache="0" sizset="30"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#source">源码解析</a></li></ul></li><li sizcache="0" sizset="31"><a href="http://www.slimeden.com/2011/09/web/memcached_client_hash#reference">参考资料</a></li></ol>
<h2 sizcache="0" sizset="32"><a name="t0"></a><a name="intro">1. memchached简介</a></h2>
<p sizcache="0" sizset="33"><a href="http://code.google.com/p/memcached/">memcached</a>是一个开源的高性能分布式内存对象缓存系统。<br />其实思想还是比较简单的，实现包括server端（memcached开源项目一般只单指server端）和client端两部分:</p>
<ul sizcache="0" sizset="34"><li>server端本质是一个<strong>in-memory key-value store</strong>，通过在内存中维护一个大的hashmap用来存储小块的任意数据，对外通过统一的简单接口（memcached protocol）来提供操作。</li><li sizcache="0" sizset="34">client端是一个library，负责处理<a href="http://code.google.com/p/memcached/wiki/Start#Wire_Protocols">memcached protocol</a>的网络通信细节，与memcached server通信，针对各种语言的不同实现分装了易用的API实现了与不同语言平台的集成。</li><li>web系统则通过client库来使用memcached进行对象缓存。</li></ul>
<h2 sizcache="0" sizset="35"><a name="t1"></a><a name="hash">2. hash</a></h2>
<p>memcached的分布式主要体现在client端，对于server端，仅仅是部署多个memcached server组成集群，每个server独自维护自己的数据（互相之间没有任何通信），通过daemon监听端口等待client端的请求。<br />而在client端，通过一致的hash算法，将要存储的数据分布到某个特定的server上进行存储，后续读取查询使用同样的hash算法即可定位。</p>
<p sizcache="0" sizset="36">client端可以采用各种hash算法来定位server：<br /><a name="mode">取模</a><br />最简单的hash算法</p>
<p style="text-align: center"><strong>targetServer = serverList[hash(key) % serverList.size]</strong></p>
<p>直接用key的hash值（计算key的hash值的方法可以自由选择，比如算法CRC32、MD5,甚至本地hash系统，如java的hashcode）模上server总数来定位目标server。这种算法不仅简单，而且具有不错的随机分布特性。</p>
<p>但是问题也很明显，server总数不能轻易变化。因为如果增加/减少memcached server的数量，对原先存储的所有key的后续查询都将定位到别的server上，导致所有的cache都不能被命中而失效。</p>
<p sizcache="0" sizset="37"><a name="consist">一致性hash</a><br />为了解决这个问题，需要采用<a href="http://en.wikipedia.org/wiki/Consistent_hashing">一致性hash算法</a>（consistent hash）<br />相对于取模的算法，一致性hash算法除了计算key的hash值外，还会计算每个server对应的hash值，然后将这些hash值映射到一个有限的值域上（比如0~2^32）。通过寻找hash值大于hash(key)的最小server作为存储该key数据的目标server。如果找不到，则直接把具有最小hash值的server作为目标server。</p>
<p sizcache="0" sizset="39">为了方便理解，可以把这个有限值域理解成一个环，值顺时针递增。<br /><a href="http://www.slimeden.com/wp-content/uploads/2011/09/12.png"><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="104" alt="circle space" src="http://www.codeproject.com/KB/recipes/lib-conhash/circle.JPG" width="91" /></a><br />如上图所示，集群中一共有5个memcached server，已通过server的hash值分布到环中。</p>
<p sizcache="0" sizset="40">如果现在有一个写入cache的请求，首先计算x=hash(key)，映射到环中，然后从x顺时针查找，把找到的第一个server作为目标server来存储cache，如果超过了2^32仍然找不到，则命中第一个server。比如x的值介于A~B之间，那么命中的server节点应该是B节点<br /><a href="http://images.cnblogs.com/cnblogs_com/coser/201111/201111271652114531.png"><img title="image" style="border-top-width: 0px; padding-right: 0px; display: inline; padding-left: 0px; border-left-width: 0px;background-image: none; border-bottom-width: 0px; width: 456px; padding-top: 0px; height: 260px; border-right-width: 0px" height="260" alt="image" src="http://images.cnblogs.com/cnblogs_com/coser/201111/201111271652114498.png" width="456" border="0" /></a><br />可以看到，通过这种算法，对于同一个key，存储和后续的查询都会定位到同一个memcached server上。</p>
<p sizcache="0" sizset="41">那么它是怎么解决增/删server导致的cache不能命中的问题呢？<br />假设，现在增加一个server F，如下图<br /><img style="width: 350px; height: 199px" height="199" alt="" src="http://www.blogjava.net/images/blogjava_net/hao446tian/hash.jpg" width="350" border="0" longdesc="" /><br /><a href="http://www.slimeden.com/wp-content/uploads/2011/09/3.png"></a><br />此时，cache不能命中的问题仍然存在，但是只存在于B~F之间的位置（由C变成了F），其他位置（包括F~C）的cache的命中不受影响（删除server的情况类似）。尽管仍然有cache不能命中的存在，但是相对于取模的方式已经大幅减少了不能命中的cache数量。</p>
<p sizcache="0" sizset="42"><a name="virtual">虚拟节点</a><br />但是，这种算法相对于取模方式也有一个缺陷：当server数量很少时，很可能他们在环中的分布不是特别均匀，进而导致cache不能均匀分布到所有的server上。<br /><a href="http://www.slimeden.com/wp-content/uploads/2011/09/4.png"><img title="点击查看源网页" style="left: 179px; width: 452px; top: 165px; height: 333px" height="333" src="http://hiphotos.baidu.com/moonlitshiny/pic/item/754767b60f2442a7efe2a811d143ad4bd01302b5.jpg" width="452" __loaded="true" __scrollbinded="true" firstheight="386px" firstwidth="526px" firstrate="1" middlex="179px" middley="165.5px" zoomrate="1" srcheight="386" srcwidth="526"  alt="" /></a><br />如图，一共有3台server &#8211; 1，2，4。命中4的几率远远高于1和2。<br />为解决这个问题，需要使用虚拟节点的思想：为每个物理节点（server）在环上分配100～200个点，这样环上的节点较多，就能抑制分布不均匀。<br />当为cache定位目标server时，如果定位到虚拟节点上，就表示cache真正的存储位置是在该虚拟节点代表的实际物理server上。</p>
<p>另外，如果每个实际server的负载能力不同，可以赋予不同的权重，根据权重分配不同数量的虚拟节点。</p>
<p sizcache="0" sizset="44"><a name="source">源码解析</a><br />下面结合一个java的memcached client（<a href="https://github.com/gwhalin/Memcached-Java-Client">gwhalin / Memcached-Java-Client</a>）的源码来看一下consistent hash的实现。<br />首先看server的分布：</p>
<div class="syntaxhighlighter  " id="highlighter_668771" sizcache="1" sizset="0">
<div class="lines" sizcache="1" sizset="0">
<div class="line alt1" sizcache="1" sizset="0">
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span class="comment">//&nbsp;采用有序map来模拟环 </span><span>&nbsp;&nbsp;</span></li><li class=""><span></span><span class="keyword">this</span><span>.consistentBuckets&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;TreeMap();&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>MessageDigest&nbsp;md5&nbsp;=&nbsp;MD5.get();</span><span class="comment">//用MD5来计算key和server的hash值 </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span></span><span class="comment">//&nbsp;计算总权重 </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span></span><span class="keyword">if</span><span>&nbsp;(&nbsp;</span><span class="keyword">this</span><span>.totalWeight&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(&nbsp;</span><span class="datatypes">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;</span><span class="keyword">this</span><span>.weights.length;&nbsp;i++&nbsp;)&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.totalWeight&nbsp;+=&nbsp;(&nbsp;</span><span class="keyword">this</span><span>.weights[i]&nbsp;==&nbsp;null&nbsp;)&nbsp;?&nbsp;1&nbsp;:&nbsp;</span><span class="keyword">this</span><span>.weights[i];&nbsp;&nbsp;</span></span></li><li class="alt"><span>}&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;</span><span class="keyword">this</span><span>.weights&nbsp;==&nbsp;null&nbsp;)&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">this</span><span>.totalWeight&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.servers.length;&nbsp;&nbsp;</span></span></li><li class="alt"><span>}&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span></span><span class="comment">//&nbsp;为每个server分配虚拟节点 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span></span><span class="keyword">for</span><span>&nbsp;(&nbsp;</span><span class="datatypes">int</span><span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;servers.length;&nbsp;i++&nbsp;)&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;计算当前server的权重 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="datatypes">int</span><span>&nbsp;thisWeight&nbsp;=&nbsp;1;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(&nbsp;</span><span class="keyword">this</span><span>.weights&nbsp;!=&nbsp;null&nbsp;&amp;&amp;&nbsp;</span><span class="keyword">this</span><span>.weights[i]&nbsp;!=&nbsp;null&nbsp;)&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thisWeight&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.weights[i];&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;factor用来控制每个server分配的虚拟节点数量 </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;权重都相同时，factor=40 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;权重不同时，factor=40*server总数*该server权重所占的百分比 </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;总的来说，权重越大，factor越大，可以分配越多的虚拟节点 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="datatypes">double</span><span>&nbsp;factor&nbsp;=&nbsp;Math.floor(&nbsp;((</span><span class="datatypes">double</span><span>)(40&nbsp;*&nbsp;</span><span class="keyword">this</span><span>.servers.length&nbsp;*&nbsp;thisWeight))&nbsp;/&nbsp;(</span><span class="datatypes">double</span><span>)</span><span class="keyword">this</span><span>.totalWeight&nbsp;);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(&nbsp;</span><span class="datatypes">long</span><span>&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;factor;&nbsp;j++&nbsp;)&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;每个server有factor个hash值 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;使用server的域名或IP加上编号来计算hash值 </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;比如server&nbsp;-&nbsp;"172.45.155.25:11111"就有factor个数据用来生成hash值： </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;172.45.155.25:11111-1,&nbsp;172.45.155.25:11111-2,&nbsp;...,&nbsp;172.45.155.25:11111-factor </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;d&nbsp;=&nbsp;md5.digest(&nbsp;(&nbsp;servers[i]&nbsp;+&nbsp;</span><span class="string">"-"</span><span>&nbsp;+&nbsp;j&nbsp;).getBytes()&nbsp;);&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;每个hash值生成4个虚拟节点 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(&nbsp;</span><span class="datatypes">int</span><span>&nbsp;h&nbsp;=&nbsp;0&nbsp;;&nbsp;h&nbsp;&lt;&nbsp;4;&nbsp;h++&nbsp;)&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Long&nbsp;k&nbsp;=&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((</span><span class="datatypes">long</span><span>)(d[3+h*4]&amp;0xFF)&nbsp;&lt;&lt;&nbsp;24)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;((</span><span class="datatypes">long</span><span>)(d[2+h*4]&amp;0xFF)&nbsp;&lt;&lt;&nbsp;16)&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;((</span><span class="datatypes">long</span><span>)(d[1+h*4]&amp;0xFF)&nbsp;&lt;&lt;&nbsp;8&nbsp;)&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;((</span><span class="datatypes">long</span><span>)(d[0+h*4]&amp;0xFF));&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;在环上保存节点 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;consistentBuckets.put(&nbsp;k,&nbsp;servers[i]&nbsp;);&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//&nbsp;每个server一共分配4*factor个虚拟节点 </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>}&nbsp;&nbsp;</span></li></ol></div><pre class="cpp" style="display: none" name="code">// 采用有序map来模拟环
this.consistentBuckets = new TreeMap();

MessageDigest md5 = MD5.get();//用MD5来计算key和server的hash值

// 计算总权重
if ( this.totalWeight 	for ( int i = 0; i &lt; this.weights.length; i++ )
		this.totalWeight += ( this.weights[i] == null ) ? 1 : this.weights[i];
} else if ( this.weights == null ) {
	this.totalWeight = this.servers.length;
}

// 为每个server分配虚拟节点
for ( int i = 0; i &lt; servers.length; i++ ) {
	// 计算当前server的权重
	int thisWeight = 1;
	if ( this.weights != null &amp;&amp; this.weights[i] != null )
		thisWeight = this.weights[i];

	// factor用来控制每个server分配的虚拟节点数量
	// 权重都相同时，factor=40
	// 权重不同时，factor=40*server总数*该server权重所占的百分比
	// 总的来说，权重越大，factor越大，可以分配越多的虚拟节点
	double factor = Math.floor( ((double)(40 * this.servers.length * thisWeight)) / (double)this.totalWeight );

	for ( long j = 0; j &lt; factor; j++ ) {
		// 每个server有factor个hash值
		// 使用server的域名或IP加上编号来计算hash值
		// 比如server - "172.45.155.25:11111"就有factor个数据用来生成hash值：
		// 172.45.155.25:11111-1, 172.45.155.25:11111-2, ..., 172.45.155.25:11111-factor
		byte[] d = md5.digest( ( servers[i] + "-" + j ).getBytes() );

		// 每个hash值生成4个虚拟节点
		for ( int h = 0 ; h &lt; 4; h++ ) {
			Long k =
				((long)(d[3+h*4]&amp;0xFF) &lt;&lt; 24)
			      | ((long)(d[2+h*4]&amp;0xFF) &lt;&lt; 16)
			      | ((long)(d[1+h*4]&amp;0xFF) &lt;&lt; 8 )
			      | ((long)(d[0+h*4]&amp;0xFF));

			// 在环上保存节点
			consistentBuckets.put( k, servers[i] );
		}

	}
	// 每个server一共分配4*factor个虚拟节点
}
</pre><br /><br /></div></div></div>
<p>每个server根据权重获得一个虚拟节点数量控制因子factor，然后由services[i]+&#8221;-&#8221;+i来生成factor个hash值，生成hash值时采用MD5算法。<br />由于MD5长度是16个字节，正好划分成4段，每段4字节，这样每段就对应一个虚拟节点。linex-liney通过把这一段的4字节拼装成连续的32bit，作为低32位拉升为一个Long。</p>
<p>为key定位cache存储的server：</p>
<div class="syntaxhighlighter  " id="highlighter_665561" sizcache="1" sizset="1">
<div class="lines" sizcache="1" sizset="1">
<div class="line alt2" sizcache="1" sizset="1">
<div class="dp-highlighter bg_cpp">
<div class="bar">
<div class="tools"><strong>[cpp]</strong> <a class="ViewSource" title="view plain" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">view plain</font></u></a><a class="CopyToClipboard" title="copy" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">copy</font></u></a><a class="PrintSource" title="print" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">print</font></u></a><a class="About" title="?" href="http://blog.csdn.net/ohmygirl/article/details/7755334#"><u><font color="#0000ff">?</font></u></a></div></div>
<ol class="dp-cpp"><li class="alt"><span class="comment">//&nbsp;用MD5来计算key的hash值 </span><span>&nbsp;&nbsp;</span></li><li class=""><span>MessageDigest&nbsp;md5&nbsp;=&nbsp;MD5.get();&nbsp;&nbsp;</span></li><li class="alt"><span>md5.reset();&nbsp;&nbsp;</span></li><li class=""><span>md5.update(&nbsp;key.getBytes()&nbsp;);&nbsp;&nbsp;</span></li><li class="alt"><span>byte[]&nbsp;bKey&nbsp;=&nbsp;md5.digest();&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;</span></li><li class="alt"><span></span><span class="comment">//&nbsp;取MD5值的低32位作为key的hash值 </span><span>&nbsp;&nbsp;</span></span></li><li class=""><span></span><span class="datatypes">long</span><span>&nbsp;hv&nbsp;=&nbsp;((</span><span class="datatypes">long</span><span>)(bKey[3]&amp;0xFF)&nbsp;&lt;&lt;&nbsp;24)&nbsp;|&nbsp;((</span><span class="datatypes">long</span><span>)(bKey[2]&amp;0xFF)&nbsp;&lt;&lt;&nbsp;16)&nbsp;|&nbsp;((</span><span class="datatypes">long</span><span>)(bKey[1]&amp;0xFF)&nbsp;&lt;&lt;&nbsp;8&nbsp;)&nbsp;|&nbsp;(</span><span class="datatypes">long</span><span>)(bKey[0]&amp;0xFF);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span></span><span class="comment">//&nbsp;hv的tailMap的第一个虚拟节点对应的即是目标server </span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>SortedMap&nbsp;tmap&nbsp;=&nbsp;</span><span class="keyword">this</span><span>.consistentBuckets.tailMap(&nbsp;hv&nbsp;);&nbsp;&nbsp;</span></span></li><li class=""><span></span><span class="keyword">return</span><span>&nbsp;(&nbsp;tmap.isEmpty()&nbsp;)&nbsp;?&nbsp;</span><span class="keyword">this</span><span>.consistentBuckets.firstKey()&nbsp;:&nbsp;tmap.firstKey();&nbsp;&nbsp;</span></span></li></ol></div><pre class="cpp" style="display: none" name="code">// 用MD5来计算key的hash值
MessageDigest md5 = MD5.get();
md5.reset();
md5.update( key.getBytes() );
byte[] bKey = md5.digest();

// 取MD5值的低32位作为key的hash值
long hv = ((long)(bKey[3]&amp;0xFF) &lt;&lt; 24) | ((long)(bKey[2]&amp;0xFF) &lt;&lt; 16) | ((long)(bKey[1]&amp;0xFF) &lt;&lt; 8 ) | (long)(bKey[0]&amp;0xFF);

// hv的tailMap的第一个虚拟节点对应的即是目标server
SortedMap tmap = this.consistentBuckets.tailMap( hv );
return ( tmap.isEmpty() ) ? this.consistentBuckets.firstKey() : tmap.firstKey();
</pre><br /><br /></div></div></div>
<h2 sizcache="0" sizset="46"><a name="t2"></a><a name="reference">3. 参考资料</a></h2>
<ol sizcache="0" sizset="47"><li sizcache="0" sizset="47">首次提出consistent hash的论文 &#8211; <a href="http://portal.acm.org/citation.cfm?id=258660">&#8220;Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web&#8221;</a></li><li sizcache="0" sizset="48">Consistent hashing Wiki &#8211; <a href="http://en.wikipedia.org/wiki/Consistent_hashing">http://en.wikipedia.org/wiki/Consistent_hashing</a></li><li sizcache="0" sizset="49"><a href="http://www.audioscrobbler.net/development/ketama/">Ketama: Consistent Hashing</a></li><li sizcache="0" sizset="50"><a href="http://memcached.org/">memcached开源项目主页</a></li><li sizcache="0" sizset="51"><a href="http://code.google.com/p/memcached/">memcached google code主页</a></li><li sizcache="0" sizset="52"><a href="https://github.com/gwhalin/Memcached-Java-Client">gwhalin / Memcached-Java-Client主页</a></li></ol><img src ="http://www.blogjava.net/hao446tian/aggbug/394858.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2013-01-29 11:26 <a href="http://www.blogjava.net/hao446tian/archive/2013/01/29/394858.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一致性hash算法 - consistent hashing (转)</title><link>http://www.blogjava.net/hao446tian/archive/2013/01/29/394859.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Tue, 29 Jan 2013 03:26:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2013/01/29/394859.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/394859.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2013/01/29/394859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/394859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/394859.html</trackback:ping><description><![CDATA[<h1 style="text-align: center" sizset="44" sizcache="2"><a name="t0"></a><span style="font-family: 宋体">一致性</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法（</span> <span lang="EN-US">consistent hashing</span> <span style="font-family: 宋体">）</span> </h1>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">张亮</span> </p>
<p class="MsoNormal" style="text-indent: 21pt" sizset="45" sizcache="2"><span lang="EN-US">consistent hashing</span> <span style="font-family: 宋体">算法早在</span> <span lang="EN-US">1997</span> <span style="font-family: 宋体">年就在论文</span> <strong sizset="45" sizcache="2"><span lang="EN-US" sizset="45" sizcache="2"><a href="http://portal.acm.org/citation.cfm?id=258660" target="_blank">Consistent hashing and random trees</a> </span></strong><span style="font-family: 宋体">中被提出，目前在</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">系统中应用越来越广泛；</span> </p>
<h2 sizset="46" sizcache="2"><a name="t1"></a><span lang="EN-US">1</span> 基本场景</h2>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">比如你有</span> <span lang="EN-US">N</span> <span style="font-family: 宋体">个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">服务器（后面简称</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">），那么如何将一个对象</span> <span lang="EN-US">object</span> <span style="font-family: 宋体">映射到</span> <span lang="EN-US">N</span> <span style="font-family: 宋体">个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">上呢，你很可能会采用类似下面的通用方法计算</span> <span lang="EN-US">object</span> <span style="font-family: 宋体">的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值，然后均匀的映射到到</span> <span lang="EN-US">N</span> <span style="font-family: 宋体">个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">；</span> </p>
<p class="MsoNormal"><span lang="EN-US">hash(object)%N</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">一切都运行正常，再考虑如下的两种情况；</span> </p>
<p class="MsoNormal"><span lang="EN-US">1 </span><span style="font-family: 宋体">一个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">服务器</span> <span lang="EN-US">m down</span> <span style="font-family: 宋体">掉了（在实际应用中必须要考虑这种情况），这样所有映射到</span> <span lang="EN-US">cache m</span> <span style="font-family: 宋体">的对象都会失效，怎么办，需要把</span> <span lang="EN-US">cache m</span> <span style="font-family: 宋体">从</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">中移除，这时候</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">是</span> <span lang="EN-US">N-1</span> <span style="font-family: 宋体">台，映射公式变成了</span> <span lang="EN-US">hash(object)%(N-1)</span> <span style="font-family: 宋体">；</span> </p>
<p class="MsoNormal"><span lang="EN-US">2 </span><span style="font-family: 宋体">由于访问加重，需要添加</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">，这时候</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">是</span> <span lang="EN-US">N+1</span> <span style="font-family: 宋体">台，映射公式变成了</span> <span lang="EN-US">hash(object)%(N+1)</span> <span style="font-family: 宋体">；</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span lang="EN-US">1</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">2</span> <span style="font-family: 宋体">意味着什么？这意味着突然之间几乎所有的</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">都失效了。对于服务器而言，这是一场灾难，洪水般的访问都会直接冲向后台服务器；</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">再来考虑第三个问题，由于硬件能力越来越强，你可能想让后面添加的节点多做点活，显然上面的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法也做不到。</span> </p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span> <span style="font-family: 宋体">有什么方法可以改变这个状况呢，这就是</span> <span lang="EN-US">consistent hashing...</span> </p>
<h2 sizset="47" sizcache="2"><a name="t2"></a><span lang="EN-US">2 hash</span> 算法和单调性</h2>
<p class="MsoNormal"><span style="font-family: 宋体">　　</span> <span lang="EN-US">Hash</span> <span style="font-family: 宋体">算法的一个衡量指标是单调性（</span> <span lang="EN-US">Monotonicity</span> <span style="font-family: 宋体">），定义如下：</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">　　单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中，又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去，而不会被映射到旧的缓冲集合中的其他缓冲区。</span> <span lang="EN-US"></span></p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">容易看到，上面的简单</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法</span> <span lang="EN-US">hash(object)%N</span> <span style="font-family: 宋体">难以满足单调性要求。</span> </p>
<h2 sizset="48" sizcache="2"><a name="t3"></a><span lang="EN-US">3 consistent hashing</span> 算法的原理</h2>
<p class="MsoNormal" style="text-indent: 21pt"><span lang="EN-US">consistent hashing</span> <span style="font-family: 宋体">是一种</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法，简单的说，在移除</span> <span lang="EN-US">/</span> <span style="font-family: 宋体">添加一个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">时，它能够尽可能小的改变已存在</span> <span lang="EN-US">key</span> <span style="font-family: 宋体">映射关系，尽可能的满足单调性的要求。</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">下面就来按照</span> <span lang="EN-US">5</span> <span style="font-family: 宋体">个步骤简单讲讲</span> <span lang="EN-US">consistent hashing</span> <span style="font-family: 宋体">算法的基本原理。</span> </p>
<h3 sizset="49" sizcache="2"><a name="t4"></a><span lang="EN-US">3.1</span> 环形<span lang="EN-US">hash</span> 空间</h3>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">考虑通常的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法都是将</span> <span lang="EN-US">value</span> <span style="font-family: 宋体">映射到一个</span> <span lang="EN-US">32</span> <span style="font-family: 宋体">为的</span> <span lang="EN-US">key</span> <span style="font-family: 宋体">值，也即是</span> <span lang="EN-US">0~2^32-1</span> <span style="font-family: 宋体">次方的数值空间；我们可以将这个空间想象成一个首（</span> <span lang="EN-US">0</span> <span style="font-family: 宋体">）尾（</span> <span lang="EN-US">2^32-1</span> <span style="font-family: 宋体">）相接的圆环，如下面图</span> <span lang="EN-US">1</span> <span style="font-family: 宋体">所示的那样。</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
 <v:stroke joinstyle="miter" />
 <v:formulas>
  <v:f eqn="if lineDrawn pixelLineWidth 0" />
  <v:f eqn="sum @0 1 0" />
  <v:f eqn="sum 0 0 @1" />
  <v:f eqn="prod @2 1 2" />
  <v:f eqn="prod @3 21600 pixelWidth" />
  <v:f eqn="prod @3 21600 pixelHeight" />
  <v:f eqn="sum @0 0 1" />
  <v:f eqn="prod @6 1 2" />
  <v:f eqn="prod @7 21600 pixelWidth" />
  <v:f eqn="sum @8 21600 0" />
  <v:f eqn="prod @7 21600 pixelHeight" />
  <v:f eqn="sum @10 21600 0" />
 </v:formulas>
 <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" />
 <o:lock v:ext="edit" aspectratio="t" />
</v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:68.25pt;
 height:78pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image001.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image001.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="104" alt="circle space" src="http://www.codeproject.com/KB/recipes/lib-conhash/circle.JPG" width="91" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">1</span> <span style="font-family: 宋体">环形</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">空间</span> </p>
<h3 sizset="50" sizcache="2"><a name="t5"></a><span lang="EN-US">3.2 </span>把对象映射到<span lang="EN-US">hash</span> 空间</h3>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">接下来考虑</span> <span lang="EN-US">4</span> <span style="font-family: 宋体">个对象</span> <span lang="EN-US">object1~object4</span> <span style="font-family: 宋体">，通过</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">函数计算出的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值</span> <span lang="EN-US">key</span> <span style="font-family: 宋体">在环上的分布如图</span> <span lang="EN-US">2</span> <span style="font-family: 宋体">所示。</span> </p>
<p class="MsoNormal"><span lang="EN-US">hash(object1) = key1;</span> </p>
<p class="MsoNormal"><span lang="EN-US">&#8230; &#8230;</span> </p>
<p class="MsoNormal"><span lang="EN-US">hash(object4) = key4;</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shape id="_x0000_i1026" type="#_x0000_t75" style='width:175.5pt;height:189.75pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image003.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image003.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="253" alt="object" src="http://www.codeproject.com/KB/recipes/lib-conhash/object.JPG" width="234" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">2 4</span> <span style="font-family: 宋体">个对象的</span> <span lang="EN-US">key</span> <span style="font-family: 宋体">值分布</span> </p>
<h3 sizset="51" sizcache="2"><a name="t6"></a><span lang="EN-US">3.3 </span>把<span lang="EN-US">cache</span> 映射到<span lang="EN-US">hash</span> 空间</h3>
<p class="MsoNormal" style="text-indent: 21pt"><span lang="EN-US">Consistent hashing</span> <span style="font-family: 宋体">的基本思想就是将对象和</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">都映射到同一个</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">数值空间中，并且使用相同的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">假设当前有</span> <span lang="EN-US">A,B</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">C</span> <span style="font-family: 宋体">共</span> <span lang="EN-US">3</span> <span style="font-family: 宋体">台</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">，那么其映射结果将如图</span> <span lang="EN-US">3</span> <span style="font-family: 宋体">所示，他们在</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">空间中，以对应的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值排列。</span> </p>
<p class="MsoNormal"><span lang="EN-US">hash(cache A) = key A;</span> </p>
<p class="MsoNormal"><span lang="EN-US">&#8230; &#8230;</span> </p>
<p class="MsoNormal"><span lang="EN-US">hash(cache C) = key C;</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shape id="_x0000_i1027" type="#_x0000_t75" style='width:212.25pt;height:189.75pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image005.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image005.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="253" alt="cache" src="http://www.codeproject.com/KB/recipes/lib-conhash/cache.JPG" width="283" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">3 cache</span> <span style="font-family: 宋体">和对象的</span> <span lang="EN-US">key</span> <span style="font-family: 宋体">值分布</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US">&nbsp;</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">说到这里，顺便提一下</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">计算，一般的方法可以使用</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">机器的</span> <span lang="EN-US">IP</span> <span style="font-family: 宋体">地址或者机器名作为</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">输入。</span> </p>
<h3 sizset="52" sizcache="2"><a name="t7"></a><span lang="EN-US">3.4 </span>把对象映射到<span lang="EN-US">cache</span> </h3>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">现在</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">和对象都已经通过同一个</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">算法映射到</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">数值空间中了，接下来要考虑的就是如何将对象映射到</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">上面了。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">在这个环形空间中，如果沿着顺时针方向从对象的</span> <span lang="EN-US">key</span> <span style="font-family: 宋体">值出发，直到遇见一个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">，那么就将该对象存储在这个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">上，因为对象和</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值是固定的，因此这个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">必然是唯一和确定的。这样不就找到了对象和</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">的映射方法了吗？！</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">依然继续上面的例子（参见图</span> <span lang="EN-US">3</span> <span style="font-family: 宋体">），那么根据上面的方法，对象</span> <span lang="EN-US">object1</span> <span style="font-family: 宋体">将被存储到</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">上；</span> <span lang="EN-US">object2</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">object3</span> <span style="font-family: 宋体">对应到</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">；</span> <span lang="EN-US">object4</span> <span style="font-family: 宋体">对应到</span> <span lang="EN-US">cache B</span> <span style="font-family: 宋体">；</span> </p>
<h3 sizset="53" sizcache="2"><a name="t8"></a><span lang="EN-US">3.5 </span>考察<span lang="EN-US">cache</span> 的变动</h3>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">前面讲过，通过</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">然后求余的方法带来的最大问题就在于不能满足单调性，当</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">有所变动时，</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">会失效，进而对后台服务器造成巨大的冲击，现在就来分析分析</span> <span lang="EN-US">consistent hashing</span> <span style="font-family: 宋体">算法。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><strong><span lang="EN-US">3.5.1 </span></strong><strong><span style="font-family: 宋体">移除</span> <span lang="EN-US">cache</span> </strong></p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">考虑假设</span> <span lang="EN-US">cache B</span> <span style="font-family: 宋体">挂掉了，根据上面讲到的映射方法，这时受影响的将仅是那些沿</span> <span lang="EN-US">cache B</span> <span style="font-family: 宋体">逆时针遍历直到下一个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">（</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">）之间的对象，也即是本来映射到</span> <span lang="EN-US">cache B</span> <span style="font-family: 宋体">上的那些对象。</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">因此这里仅需要变动对象</span> <span lang="EN-US">object4</span> <span style="font-family: 宋体">，将其重新映射到</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">上即可；参见图</span> <span lang="EN-US">4</span> <span style="font-family: 宋体">。</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shape id="_x0000_i1028" type="#_x0000_t75" style='width:212.25pt;height:189.75pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image007.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image007.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="253" alt="remove" src="http://www.codeproject.com/KB/recipes/lib-conhash/remove.JPG" width="283" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">4 Cache B</span> <span style="font-family: 宋体">被移除后的</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">映射</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><strong><span lang="EN-US">3.5.2 </span></strong><strong><span style="font-family: 宋体">添加</span> <span lang="EN-US">cache</span> </strong></p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">再考虑添加一台新的</span> <span lang="EN-US">cache D</span> <span style="font-family: 宋体">的情况，假设在这个环形</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">空间中，</span> <span lang="EN-US">cache D</span> <span style="font-family: 宋体">被映射在对象</span> <span lang="EN-US">object2</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">object3</span> <span style="font-family: 宋体">之间。这时受影响的将仅是那些沿</span> <span lang="EN-US">cache D</span> <span style="font-family: 宋体">逆时针遍历直到下一个</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">（</span> <span lang="EN-US">cache B</span> <span style="font-family: 宋体">）之间的对象（它们是也本来映射到</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">上对象的一部分），将这些对象重新映射到</span> <span lang="EN-US">cache D</span> <span style="font-family: 宋体">上即可。</span> </p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">因此这里仅需要变动对象</span> <span lang="EN-US">object2</span> <span style="font-family: 宋体">，将其重新映射到</span> <span lang="EN-US">cache D</span> <span style="font-family: 宋体">上；参见图</span> <span lang="EN-US">5</span> <span style="font-family: 宋体">。</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shape id="_x0000_i1029" type="#_x0000_t75" style='width:212.25pt;height:189.75pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image009.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image009.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="253" alt="add" src="http://www.codeproject.com/KB/recipes/lib-conhash/add.JPG" width="283" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">5 </span><span style="font-family: 宋体">添加</span> <span lang="EN-US">cache D</span> <span style="font-family: 宋体">后的映射关系</span> </p>
<h2 sizset="54" sizcache="2"><a name="t9"></a><span lang="EN-US">4 </span>虚拟节点</h2>
<p class="MsoNormal"><span style="font-family: 宋体">考量</span> <span lang="EN-US">Hash</span> <span style="font-family: 宋体">算法的另一个指标是平衡性</span> <span lang="EN-US">(Balance)</span> <span style="font-family: 宋体">，定义如下：</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">平衡性</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">　　平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去，这样可以使得所有的缓冲空间都得到利用。</span> </p>
<p class="MsoNormal"><span lang="EN-US">hash</span> <span style="font-family: 宋体">算法并不是保证绝对的平衡，如果</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">较少的话，对象并不能被均匀的映射到</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">上，比如在上面的例子中，仅部署</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">的情况下，在</span> <span lang="EN-US">4</span> <span style="font-family: 宋体">个对象中，</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">仅存储了</span> <span lang="EN-US">object1</span> <span style="font-family: 宋体">，而</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">则存储了</span> <span lang="EN-US">object2</span> <span style="font-family: 宋体">、</span> <span lang="EN-US">object3</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">object4</span> <span style="font-family: 宋体">；分布是很不均衡的。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">为了解决这种情况，</span> <span lang="EN-US">consistent hashing</span> <span style="font-family: 宋体">引入了&#8220;虚拟节点&#8221;的概念，它可以如下定义：</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">&#8220;虚拟节点&#8221;（</span> <span lang="EN-US">virtual node</span> <span style="font-family: 宋体">）是实际节点在</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">空间的复制品（</span> <span lang="EN-US">replica</span> <span style="font-family: 宋体">），一实际个节点对应了若干个&#8220;虚拟节点&#8221;，这个对应个数也成为&#8220;复制个数&#8221;，&#8220;虚拟节点&#8221;在</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">空间中以</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值排列。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">仍以仅部署</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">的情况为例，在图</span> <span lang="EN-US">4</span> <span style="font-family: 宋体">中我们已经看到，</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">分布并不均匀。现在我们引入虚拟节点，并设置&#8220;复制个数&#8221;为</span> <span lang="EN-US">2</span> <span style="font-family: 宋体">，这就意味着一共会存在</span> <span lang="EN-US">4</span> <span style="font-family: 宋体">个&#8220;虚拟节点&#8221;，</span> <span lang="EN-US">cache A1, cache A2</span> <span style="font-family: 宋体">代表了</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">；</span> <span lang="EN-US">cache C1, cache C2</span> <span style="font-family: 宋体">代表了</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">；假设一种比较理想的情况，参见图</span> <span lang="EN-US">6</span> <span style="font-family: 宋体">。</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shape id="_x0000_i1030" type="#_x0000_t75" style='width:212.25pt;height:189.75pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image011.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image011.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="253" alt="virtual nodes" src="http://www.codeproject.com/KB/recipes/lib-conhash/virtual.JPG" width="283" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">6 </span><span style="font-family: 宋体">引入&#8220;虚拟节点&#8221;后的映射关系</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US">&nbsp;</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">此时，对象到&#8220;虚拟节点&#8221;的映射关系为：</span> <span lang="EN-US"></span></p>
<p class="MsoNormal"><span lang="EN-US">objec1-&gt;cache A2</span> <span style="font-family: 宋体">；</span> <span lang="EN-US">objec2-&gt;cache A1</span> <span style="font-family: 宋体">；</span> <span lang="EN-US">objec3-&gt;cache C1</span> <span style="font-family: 宋体">；</span> <span lang="EN-US">objec4-&gt;cache C2</span> <span style="font-family: 宋体">；</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">因此对象</span> <span lang="EN-US">object1</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">object2</span> <span style="font-family: 宋体">都被映射到了</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">上，而</span> <span lang="EN-US">object3</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">object4</span> <span style="font-family: 宋体">映射到了</span> <span lang="EN-US">cache C</span> <span style="font-family: 宋体">上；平衡性有了很大提高。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">引入&#8220;虚拟节点&#8221;后，映射关系就从</span> <span lang="EN-US">{</span> <span style="font-family: 宋体">对象</span> <span lang="EN-US">-&gt;</span> <span style="font-family: 宋体">节点</span> <span lang="EN-US">}</span> <span style="font-family: 宋体">转换到了</span> <span lang="EN-US">{</span> <span style="font-family: 宋体">对象</span> <span lang="EN-US">-&gt;</span> <span style="font-family: 宋体">虚拟节点</span> <span lang="EN-US">}</span> <span style="font-family: 宋体">。查询物体所在</span> <span lang="EN-US">cache</span> <span style="font-family: 宋体">时的映射关系如图</span> <span lang="EN-US">7</span> <span style="font-family: 宋体">所示。</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US"><!--    [if gte vml 1]><v:shape id="_x0000_i1031" type="#_x0000_t75" style='width:341.25pt;height:150pt'>
 <v:imagedata src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image013.png" mce_src="file:///E:/tmp/APPLIC~1/msohtml1/01/clip_image013.png" o:title="" />
</v:shape><![endif]--><!--    [if !vml]--><img style="border-top-width: 0pt; border-left-width: 0pt; border-bottom-width: 0pt; border-right-width: 0pt" height="232" alt="map" src="http://www.codeproject.com/KB/recipes/lib-conhash/map.JPG" width="529" /> <!--    [endif]--></span></p>
<p class="MsoNormal" style="text-align: center" align="center"><span style="font-family: 宋体">图</span> <span lang="EN-US">7 </span><span style="font-family: 宋体">查询对象所在</span> <span lang="EN-US">cache</span> </p>
<p class="MsoNormal" style="text-align: center" align="center"><span lang="EN-US">&nbsp;</span> </p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体">&#8220;虚拟节点&#8221;的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">计算可以采用对应节点的</span> <span lang="EN-US">IP</span> <span style="font-family: 宋体">地址加数字后缀的方式。例如假设</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">的</span> <span lang="EN-US">IP</span> <span style="font-family: 宋体">地址为</span> <span lang="EN-US">202.168.14.241</span> <span style="font-family: 宋体">。</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">引入&#8220;虚拟节点&#8221;前，计算</span> <span lang="EN-US">cache A</span> <span style="font-family: 宋体">的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值：</span> </p>
<p class="MsoNormal"><span lang="EN-US">Hash(&#8220;202.168.14.241&#8221;);</span> </p>
<p class="MsoNormal"><span style="font-family: 宋体">引入&#8220;虚拟节点&#8221;后，计算&#8220;虚拟节&#8221;点</span> <span lang="EN-US">cache A1</span> <span style="font-family: 宋体">和</span> <span lang="EN-US">cache A2</span> <span style="font-family: 宋体">的</span> <span lang="EN-US">hash</span> <span style="font-family: 宋体">值：</span> </p>
<p class="MsoNormal"><span lang="EN-US">Hash(&#8220;202.168.14.241#1&#8221;);<span>&nbsp; </span>// cache A1</span> </p>
<p class="MsoNormal"><span lang="EN-US">Hash(&#8220;202.168.14.241#2&#8221;);<span>&nbsp; </span>// cache A2</span> </p>
<h2 sizset="55" sizcache="2"><a name="t10"></a><span lang="EN-US">5 </span>小结</h2>
<p class="MsoNormal" style="text-indent: 21pt"><span lang="EN-US">Consistent hashing</span> <span style="font-family: 宋体">的基本原理就是这些，具体的分布性等理论分析应该是很复杂的，不过一般也用不到。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt" sizset="56" sizcache="2"><span lang="EN-US" sizset="56" sizcache="2"><a href="http://weblogs.java.net/blog/2007/11/27/consistent-hashing">http://weblogs.java.net/blog/2007/11/27/consistent-hashing</a> </span><span style="font-family: 宋体">上面有一个</span> <span lang="EN-US">java</span> <span style="font-family: 宋体">版本的例子，可以参考。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt" sizset="57" sizcache="2"><span lang="EN-US" sizset="57" sizcache="2"><a href="http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx">http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx</a> </span><span style="font-family: 宋体">转载了一个</span> <span lang="EN-US">PHP</span> <span style="font-family: 宋体">版的实现代码。</span> </p>
<p class="MsoNormal" style="text-indent: 21pt" sizset="58" sizcache="2"><span style="font-family: 宋体" sizset="58" sizcache="2"><a href="http://www.codeproject.com/KB/recipes/lib-conhash.aspx">http://www.codeproject.com/KB/recipes/lib-conhash.aspx</a> C语言版本<br /></span></p>
<p class="MsoNormal" style="text-indent: 21pt"><span style="font-family: 宋体"><br /></span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span style="font-family: 宋体">一些参考资料地址：</span> </p>
<p class="MsoNormal" sizset="59" sizcache="2"><span lang="EN-US" sizset="59" sizcache="2"><a href="http://portal.acm.org/citation.cfm?id=258660" target="_blank">http://portal.acm.org/citation.cfm?id=258660</a> </span></p>
<p class="MsoNormal" sizset="60" sizcache="2"><span lang="EN-US" sizset="60" sizcache="2"><a href="http://en.wikipedia.org/wiki/Consistent_hashing" target="_blank">http://en.wikipedia.org/wiki/Consistent_hashing</a> </span></p>
<p class="MsoNormal" sizset="61" sizcache="2"><span lang="EN-US" sizset="61" sizcache="2"><a href="http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/" target="_blank">http://www.spiteful.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/</a> </span></p>
<p class="MsoNormal" sizset="62" sizcache="2"><span lang="EN-US" sizset="62" sizcache="2">&nbsp;<a href="http://weblogs.java.net/blog/2007/11/27/consistent-hashing">http://weblogs.java.net/blog/2007/11/27/consistent-hashing</a> </span></p>
<p class="MsoNormal" sizset="63" sizcache="2"><span lang="EN-US" sizset="63" sizcache="2"><a href="http://tech.idv2.com/2008/07/24/memcached-004/">http://tech.idv2.com/2008/07/24/memcached-004/</a> </span></p>
<p class="MsoNormal" sizset="64" sizcache="2"><span lang="EN-US" sizset="64" sizcache="2"><a href="http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx">http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx</a> </span></p>
<p class="MsoNormal"><span lang="EN-US">&nbsp;</span> </p><img src ="http://www.blogjava.net/hao446tian/aggbug/394859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2013-01-29 11:26 <a href="http://www.blogjava.net/hao446tian/archive/2013/01/29/394859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的Memcached客户端</title><link>http://www.blogjava.net/hao446tian/archive/2013/01/28/394845.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Mon, 28 Jan 2013 09:33:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2013/01/28/394845.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/394845.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2013/01/28/394845.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/394845.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/394845.html</trackback:ping><description><![CDATA[<div class="blog_content" id="blog_content"><font size="3"><span style="font-size: medium"><strong>1.memcached client for java</strong></span> <br /></font><a href="http://www.whalin.com/memcached" target="_blank">http://www.whalin.com/memcached</a> <br /><br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://koda.iteye.com/blog/471570#"></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"></a></div></div>
<ol class="dp-j"><li><span class="keyword">import</span><span>&nbsp;com.danga.MemCached.*; &nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;org.apache.log4j.*; &nbsp;&nbsp;</span></span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;TestMemcached&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*初始化SockIOPool，管理memcached的连接池*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String[]&nbsp;servers&nbsp;=&nbsp;{&nbsp;</span><span class="string">"192.168.1.20:12111"</span><span>&nbsp;}; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SockIOPool&nbsp;pool&nbsp;=&nbsp;SockIOPool.getInstance(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setServers(servers); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setFailover(</span><span class="keyword">true</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setInitConn(</span><span class="number"><font color="#c00000">10</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setMinConn(</span><span class="number"><font color="#c00000">5</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setMaxConn(</span><span class="number"><font color="#c00000">250</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setMaintSleep(</span><span class="number"><font color="#c00000">30</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setNagle(</span><span class="keyword">false</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setSocketTO(</span><span class="number"><font color="#c00000">3000</font></span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.setAliveCheck(</span><span class="keyword">true</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pool.initialize(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*建立MemcachedClient实例*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MemCachedClient&nbsp;memCachedClient&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;MemCachedClient(); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number"><font color="#c00000">0</font></span><span>;&nbsp;i&nbsp;&lt;&nbsp;</span><span class="number"><font color="#c00000">10</font></span><span>;&nbsp;i++)&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*将对象加入到memcached缓存*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;success&nbsp;=&nbsp;memCachedClient.set(</span><span class="string">""</span><span>&nbsp;+&nbsp;i,&nbsp;</span><span class="string">"Hello!"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*从memcached缓存中按key值取对象*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;result&nbsp;=&nbsp;(String)&nbsp;memCachedClient.get(</span><span class="string">""</span><span>&nbsp;+&nbsp;i); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(String.format(</span><span class="string">"set(&nbsp;%d&nbsp;):&nbsp;%s"</span><span>,&nbsp;i,&nbsp;success)); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(String.format(</span><span class="string">"get(&nbsp;%d&nbsp;):&nbsp;%s"</span><span>,&nbsp;i,&nbsp;result)); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre class="java" title="Java的Memcached客户端" style="display: none" name="code" pre_index="0" source_url="http://koda.iteye.com/blog/471570" codeable_type="Blog" codeable_id="471570">import com.danga.MemCached.*;
import org.apache.log4j.*;
public class TestMemcached {
    public static void main(String[] args) {
        /*初始化SockIOPool，管理memcached的连接池*/
        String[] servers = { "192.168.1.20:12111" };
        SockIOPool pool = SockIOPool.getInstance();
        pool.setServers(servers);
        pool.setFailover(true);
        pool.setInitConn(10);
        pool.setMinConn(5);
        pool.setMaxConn(250);
        pool.setMaintSleep(30);
        pool.setNagle(false);
        pool.setSocketTO(3000);
        pool.setAliveCheck(true);
        pool.initialize();
        /*建立MemcachedClient实例*/
        MemCachedClient memCachedClient = new MemCachedClient();
        for (int i = 0; i &lt; 10; i++) {
            /*将对象加入到memcached缓存*/
            boolean success = memCachedClient.set("" + i, "Hello!");
            /*从memcached缓存中按key值取对象*/
            String result = (String) memCachedClient.get("" + i);
            System.out.println(String.format("set( %d ): %s", i, success));
            System.out.println(String.format("get( %d ): %s", i, result));
        }
    }
}</pre><br /><br /><font size="3"><span style="font-size: medium"><strong>2.spymemcached</strong></span> <br /></font><a href="http://code.google.com/p/spymemcached" target="_blank">http://code.google.com/p/spymemcached/</a> <br /><br />用spymemcached将对象存入缓存 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://koda.iteye.com/blog/471570#"></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"></a></div></div>
<ol class="dp-j"><li><span class="keyword">import</span><span>&nbsp;java.net.InetSocketAddress; &nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.Future; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;net.spy.memcached.MemcachedClient; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;MClient&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*建立MemcachedClient&nbsp;实例，并指定memcached服务的IP地址和端口号*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MemcachedClient&nbsp;mc&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;MemcachedClient(</span><span class="keyword">new</span><span>&nbsp;InetSocketAddress(</span><span class="string">"192.168.1.20"</span><span>,&nbsp;</span><span class="number"><font color="#c00000">12111</font></span><span>)); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Future&lt;Boolean&gt;&nbsp;b&nbsp;=&nbsp;</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*将key值，过期时间(秒)和要缓存的对象set到memcached中*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b&nbsp;=&nbsp;mc.set(</span><span class="string">"neea:testDaF:ksIdno"</span><span>,&nbsp;</span><span class="number"><font color="#c00000">900</font></span><span>,&nbsp;</span><span class="string">"someObject"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(b.get().booleanValue()==</span><span class="keyword">true</span><span>){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mc.shutdown(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">catch</span><span>(Exception&nbsp;ex){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre class="java" title="Java的Memcached客户端" style="display: none" name="code" pre_index="1" source_url="http://koda.iteye.com/blog/471570" codeable_type="Blog" codeable_id="471570">import java.net.InetSocketAddress;
import java.util.concurrent.Future;

import net.spy.memcached.MemcachedClient;

public class MClient {

    public static void main(String[] args){
        try{
            /*建立MemcachedClient 实例，并指定memcached服务的IP地址和端口号*/
            MemcachedClient mc = new MemcachedClient(new InetSocketAddress("192.168.1.20", 12111));
            Future&lt;Boolean&gt; b = null;
            /*将key值，过期时间(秒)和要缓存的对象set到memcached中*/
            b = mc.set("neea:testDaF:ksIdno", 900, "someObject");
            if(b.get().booleanValue()==true){
                mc.shutdown();
            }
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }
}</pre><br />用spymemcached从缓存中取得对象 <br />
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="复制代码" href="http://koda.iteye.com/blog/471570#"></a>&nbsp;<a title="收藏这段代码" href="javascript:void()"></a></div></div>
<ol class="dp-j"><li><span class="keyword">import</span><span>&nbsp;java.net.InetSocketAddress; &nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;java.util.concurrent.Future; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">import</span><span>&nbsp;net.spy.memcached.MemcachedClient; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span></span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;MClient&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*建立MemcachedClient&nbsp;实例，并指定memcached服务的IP地址和端口号*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MemcachedClient&nbsp;mc&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;MemcachedClient(</span><span class="keyword">new</span><span>&nbsp;InetSocketAddress(</span><span class="string">"192.168.1.20"</span><span>,&nbsp;</span><span class="number"><font color="#c00000">12111</font></span><span>)); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">/*按照key值从memcached中查找缓存，不存在则返回null&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li><li><span>Object&nbsp;b&nbsp;=&nbsp;mc.get(</span><span class="string">"neea:testDaF:ksIdno&nbsp;"</span><span>); &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mc.shutdown(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">catch</span><span>(Exception&nbsp;ex){ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre class="java" title="Java的Memcached客户端" style="display: none" name="code" pre_index="2" source_url="http://koda.iteye.com/blog/471570" codeable_type="Blog" codeable_id="471570">import java.net.InetSocketAddress;
import java.util.concurrent.Future;

import net.spy.memcached.MemcachedClient;

public class MClient {

    public static void main(String[] args){
        try{
            /*建立MemcachedClient 实例，并指定memcached服务的IP地址和端口号*/
            MemcachedClient mc = new MemcachedClient(new InetSocketAddress("192.168.1.20", 12111));
            /*按照key值从memcached中查找缓存，不存在则返回null */
Object b = mc.get("neea:testDaF:ksIdno ");
            mc.shutdown();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }
    }
}</pre><br />以上两种API比较 <br />memcached client for java：较早推出的memcached JAVA客户端API，应用广泛，运行比较稳定。 <br />spymemcached：A simple， asynchronous， single-threaded memcached client written in java. 支持异步，单线程的memcached客户端，用到了java1.5版本的concurrent和nio，存取速度会高于前者，但是稳定性不好，测试中常报timeOut等相关异常。 <br />由于memcached client for java发布了新版本，性能上有所提高，并且运行稳定，所以建议使用memcached client for java. <br /><br /><font size="3"><span style="font-size: medium"><strong>3. Xmemcached</strong></span> <br /></font><a href="http://code.google.com/p/xmemcached/" target="_blank">http://code.google.com/p/xmemcached/</a> <br />
<div class="quote_title">引用</div>
<div class="quote_div"><br />Xmemcached是基于java nio实现的高性能可扩展的memcached客户端。 <br /><br />实际上是基于一个nio框架 http://code.google.com/p/yanf4j/的基础上实现的(目前1.1.3是基于yanf4j 0.7.0，而1.2.0-RC1版本基于1.0-SNAPSHOT)。 </div></div><img src ="http://www.blogjava.net/hao446tian/aggbug/394845.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2013-01-28 17:33 <a href="http://www.blogjava.net/hao446tian/archive/2013/01/28/394845.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached全面剖析–3.memcached的删除机制和发展方向(转) </title><link>http://www.blogjava.net/hao446tian/archive/2013/01/26/394794.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Sat, 26 Jan 2013 03:54:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2013/01/26/394794.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/394794.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2013/01/26/394794.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/394794.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/394794.html</trackback:ping><description><![CDATA[<div>
<div class="entry-content">
<p>下面是《memcached全面剖析》的第三部分。</p>
<ul><li>发表日：2008/7/16</li><li>作者：前坂徹(Toru Maesaka)</li><li>原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0003">http://gihyo.jp/dev/feature/01/memcached/0003</a> </li></ul>
<p>前几次的文章在这里：</p>
<ul><li>第1次：<a href="http://tech.idv2.com/2008/07/10/memcached-001/">http://tech.idv2.com/2008/07/10/memcached-001/</a></li><li>第2次：<a href="http://tech.idv2.com/2008/07/11/memcached-002/">http://tech.idv2.com/2008/07/11/memcached-002/</a> </li></ul>
<p><span id="more-600"></span></p>
<p>memcached是缓存，所以数据不会永久保存在服务器上，这是向系统中引入memcached的前提。本次介绍memcached的数据删除机制，以及memcached的最新发展方向&#8212;&#8212;二进制协议（Binary Protocol）和外部引擎支持。</p>
<h1>memcached在数据删除方面有效利用资源</h1>
<h2>数据不会真正从memcached中消失</h2>
<p><a href="http://tech.idv2.com/2008/07/11/memcached-002/">上次</a>介绍过， memcached不会释放已分配的内存。记录超时后，客户端就无法再看见该记录（invisible，透明），其存储空间即可重复使用。</p>
<h2>Lazy Expiration</h2>
<p>memcached内部不会监视记录是否过期，而是在get时查看记录的时间戳，检查记录是否过期。这种技术被称为lazy（惰性）expiration。因此，memcached不会在过期监视上耗费CPU时间。</p>
<h1>LRU：从缓存中有效删除数据的原理</h1>
<p>memcached会优先使用已超时的记录的空间，但即使如此，也会发生追加新记录时空间不足的情况，此时就要使用名为 Least Recently Used（LRU）机制来分配空间。顾名思义，这是删除&#8220;最近最少使用&#8221;的记录的机制。因此，当memcached的内存空间不足时（无法从<a href="http://tech.idv2.com/2008/07/11/memcached-002/">slab class</a> 获取到新的空间时），就从最近未被使用的记录中搜索，并将其空间分配给新的记录。从缓存的实用角度来看，该模型十分理想。</p>
<p>不过，有些情况下LRU机制反倒会造成麻烦。memcached启动时通过&#8220;-M&#8221;参数可以禁止LRU，如下所示：</p><pre><code>$ memcached -M -m 1024
</code></pre>
<p>启动时必须注意的是，小写的&#8220;-m&#8221;选项是用来指定最大内存大小的。不指定具体数值则使用默认值64MB。</p>
<p>指定&#8220;-M&#8221;参数启动后，内存用尽时memcached会返回错误。话说回来，memcached毕竟不是存储器，而是缓存，所以推荐使用LRU。</p>
<h1>memcached的最新发展方向</h1>
<p>memcached的roadmap上有两个大的目标。一个是二进制协议的策划和实现，另一个是外部引擎的加载功能。</p>
<h2>关于二进制协议</h2>
<p>使用二进制协议的理由是它不需要文本协议的解析处理，使得原本高速的memcached的性能更上一层楼，还能减少文本协议的漏洞。目前已大部分实现，开发用的代码库中已包含了该功能。 memcached的下载页面上有代码库的链接。</p>
<ul><li><a href="http://danga.com/memcached/download.bml">http://danga.com/memcached/download.bml</a> </li></ul>
<h2>二进制协议的格式</h2>
<p>协议的包为24字节的帧，其后面是键和无结构数据（Unstructured Data）。实际的格式如下（引自协议文档）：</p><pre><code> Byte/     0       |       1       |       2       |       3       |   
    /              |               |               |               |   
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0/ HEADER                                                        /   
   /                                                               /   
   /                                                               /   
   /                                                               /   
   +---------------+---------------+---------------+---------------+
 24/ COMMAND-SPECIFIC EXTRAS (as needed)                           /   
  +/  (note length in th extras length header field)               /   
   +---------------+---------------+---------------+---------------+
  m/ Key (as needed)                                               /   
  +/  (note length in key length header field)                     /   
   +---------------+---------------+---------------+---------------+
  n/ Value (as needed)                                             /   
  +/  (note length is total body length header field, minus        /   
  +/   sum of the extras and key length body fields)               /   
   +---------------+---------------+---------------+---------------+
  Total 24 bytes
</code></pre>
<p>如上所示，包格式十分简单。需要注意的是，占据了16字节的头部(HEADER)分为请求头（Request Header）和响应头（Response Header）两种。头部中包含了表示包的有效性的Magic字节、命令种类、键长度、值长度等信息，格式如下：</p><pre><code>Request Header

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Magic         | Opcode        | Key length                    |
   +---------------+---------------+---------------+---------------+
  4| Extras length | Data type     | Reserved                      |
   +---------------+---------------+---------------+---------------+
  8| Total body length                                             |
   +---------------+---------------+---------------+---------------+
 12| Opaque                                                        |
   +---------------+---------------+---------------+---------------+
 16| CAS                                                           |
   |                                                               |
   +---------------+---------------+---------------+---------------+

Response Header

 Byte/     0       |       1       |       2       |       3       |
    /              |               |               |               |
   |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
   +---------------+---------------+---------------+---------------+
  0| Magic         | Opcode        | Key Length                    |
   +---------------+---------------+---------------+---------------+
  4| Extras length | Data type     | Status                        |
   +---------------+---------------+---------------+---------------+
  8| Total body length                                             |
   +---------------+---------------+---------------+---------------+
 12| Opaque                                                        |
   +---------------+---------------+---------------+---------------+
 16| CAS                                                           |
   |                                                               |
   +---------------+---------------+---------------+---------------+
</code></pre>
<p>如希望了解各个部分的详细内容，可以checkout出memcached的二进制协议的代码树，参考其中的docs文件夹中的protocol_binary.txt文档。</p>
<h2>HEADER中引人注目的地方</h2>
<p>看到HEADER格式后我的感想是，键的上限太大了！现在的memcached规格中，键长度最大为250字节，但二进制协议中键的大小用2字节表示。因此，理论上最大可使用65536字节（2<sup>16</sup>）长的键。尽管250字节以上的键并不会太常用，二进制协议发布之后就可以使用巨大的键了。</p>
<p>二进制协议从下一版本1.3系列开始支持。</p>
<h1>外部引擎支持</h1>
<p>我去年曾经试验性地将memcached的存储层改造成了可扩展的（pluggable）。</p>
<ul><li><a href="http://alpha.mixi.co.jp/blog/?p=129">http://alpha.mixi.co.jp/blog/?p=129</a> </li></ul>
<p>MySQL的Brian Aker看到这个改造之后，就将代码发到了memcached的邮件列表。 memcached的开发者也十分感兴趣，就放到了roadmap中。现在由我和 memcached的开发者Trond Norbye协同开发（规格设计、实现和测试）。和国外协同开发时时差是个大问题，但抱着相同的愿景，最后终于可以将可扩展架构的原型公布了。代码库可以从<a href="http://danga.com/memcached/download.bml">memcached的下载页面</a> 上访问。</p>
<h2>外部引擎支持的必要性</h2>
<p>世界上有许多memcached的派生软件，其理由是希望永久保存数据、实现数据冗余等，即使牺牲一些性能也在所不惜。我在开发memcached之前，在mixi的研发部也曾经考虑过重新发明memcached。</p>
<p>外部引擎的加载机制能封装memcached的网络功能、事件处理等复杂的处理。因此，现阶段通过强制手段或重新设计等方式使memcached和存储引擎合作的困难就会烟消云散，尝试各种引擎就会变得轻而易举了。</p>
<h2>简单API设计的成功的关键</h2>
<p>该项目中我们最重视的是API设计。函数过多，会使引擎开发者感到麻烦；过于复杂，实现引擎的门槛就会过高。因此，最初版本的接口函数只有13个。具体内容限于篇幅，这里就省略了，仅说明一下引擎应当完成的操作：</p>
<ul><li>引擎信息（版本等）</li><li>引擎初始化</li><li>引擎关闭</li><li>引擎的统计信息</li><li>在容量方面，测试给定记录能否保存</li><li>为item（记录）结构分配内存</li><li>释放item（记录）的内存</li><li>删除记录</li><li>保存记录</li><li>回收记录</li><li>更新记录的时间戳</li><li>数学运算处理</li><li>数据的flush </li></ul>
<p>对详细规格有兴趣的读者，可以checkout engine项目的代码，阅读器中的engine.h。</p>
<h2>重新审视现在的体系</h2>
<p>memcached支持外部存储的难点是，网络和事件处理相关的代码（核心服务器）与内存存储的代码紧密关联。这种现象也称为tightly coupled（紧密耦合）。必须将内存存储的代码从核心服务器中独立出来，才能灵活地支持外部引擎。因此，基于我们设计的API，memcached被重构成下面的样子：</p>
<p><img alt="memcached-0003-001.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0003-001.png" /></p>
<p>重构之后，我们与1.2.5版、二进制协议支持版等进行了性能对比，证实了它不会造成性能影响。</p>
<p>在考虑如何支持外部引擎加载时，让memcached进行并行控制（concurrency control）的方案是最为容易的，但是对于引擎而言，并行控制正是性能的真谛，因此我们采用了将多线程支持完全交给引擎的设计方案。</p>
<p>以后的改进，会使得memcached的应用范围更为广泛。</p>
<h1>总结</h1>
<p>本次介绍了memcached的超时原理、内部如何删除数据等，在此之上又介绍了二进制协议和外部引擎支持等memcached的最新发展方向。这些功能要到1.3版才会支持，敬请期待！</p>
<p>这是我在本连载中的最后一篇。感谢大家阅读我的文章！</p>
<p>下次由长野来介绍memcached的应用知识和应用程序兼容性等内容。</p></div><!-- .entry-content --></div><img src ="http://www.blogjava.net/hao446tian/aggbug/394794.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2013-01-26 11:54 <a href="http://www.blogjava.net/hao446tian/archive/2013/01/26/394794.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached全面剖析–2.理解memcached的内存存储(转)</title><link>http://www.blogjava.net/hao446tian/archive/2013/01/26/394792.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Sat, 26 Jan 2013 03:49:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2013/01/26/394792.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/394792.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2013/01/26/394792.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/394792.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/394792.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>下面是《memcached全面剖析》的第二部分。</p>
<ul><li>发表日：2008/7/9</li><li>作者：前坂徹(Toru Maesaka)</li><li>原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0002">http://gihyo.jp/dev/feature/01/memcached/0002</a> </li></ul>
<p><span id="more-596"></span></p>
<p>我是<a href="http://mixi.jp/">mixi株式会社</a>研究开发组的前坂徹。 <a href="http://tech.idv2.com/2008/07/10/memcached-001/">上次</a>的文章介绍了memcached是分布式的高速缓存服务器。本次将介绍memcached的内部构造的实现方式，以及内存的管理方式。另外，memcached的内部构造导致的弱点也将加以说明。</p>
<h1>Slab Allocation机制：整理内存以便重复使用</h1>
<p>最近的memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。在该机制出现以前，内存的分配是通过对所有记录简单地进行malloc和free来进行的。但是，这种方式会导致内存碎片，加重操作系统内存管理器的负担，最坏的情况下，会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。</p>
<p>下面来看看Slab Allocator的原理。下面是memcached文档中的slab allocator的目标：</p>
<blockquote>
<p>the primary goal of the slabs subsystem in memcached was to eliminate memory fragmentation issues totally by using fixed-size memory chunks coming from a few predetermined size classes.</p></blockquote>
<p>也就是说，Slab Allocator的基本原理是按照预先规定的大小，将分配的内存分割成特定长度的块，以完全解决内存碎片问题。</p>
<p>Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块（chunk），并把尺寸相同的块分成组（chunk的集合）（图1）。</p>
<p><img alt="memcached-0002-01.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0002-01.png" /></p>
<p>图1 Slab Allocation的构造图</p>
<p>而且，slab allocator还有重复使用已分配的内存的目的。也就是说，分配到的内存不会释放，而是重复利用。</p>
<h2>Slab Allocation的主要术语</h2>
<p><strong>Page</strong></p>
<p>分配给Slab的内存空间，默认是1MB。分配给Slab之后根据slab的大小切分成chunk。</p>
<p><strong>Chunk</strong></p>
<p>用于缓存记录的内存空间。</p>
<p><strong>Slab Class</strong></p>
<p>特定大小的chunk的组。</p>
<h1>在Slab中缓存记录的原理</h1>
<p>下面说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。</p>
<p>memcached根据收到的数据的大小，选择最适合数据大小的slab（图2）。 memcached中保存着slab内空闲chunk的列表，根据该列表选择chunk，然后将数据缓存于其中。</p>
<p><img alt="memcached-0002-02.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0002-02.png" /></p>
<p>图2 选择存储记录的组的方法</p>
<p>实际上，Slab Allocator也是有利也有弊。下面介绍一下它的缺点。</p>
<h1>Slab Allocator的缺点</h1>
<p>Slab Allocator解决了当初的内存碎片问题，但新的机制也给memcached带来了新的问题。</p>
<p>这个问题就是，由于分配的是特定长度的内存，因此无法有效利用分配的内存。例如，将100字节的数据缓存到128字节的chunk中，剩余的28字节就浪费了（图3）。</p>
<p><img alt="memcached-0002-03.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0002-03.png" /></p>
<p>图3 chunk空间的使用</p>
<p>对于该问题目前还没有完美的解决方案，但在文档中记载了比较有效的解决方案。</p>
<blockquote>
<p>The most efficient way to reduce the waste is to use a list of size classes that closely matches (if that&#8217;s at all possible) common sizes of objects that the clients of this particular installation of memcached are likely to store.</p></blockquote>
<p>就是说，如果预先知道客户端发送的数据的公用大小，或者仅缓存大小相同的数据的情况下，只要使用适合数据大小的组的列表，就可以减少浪费。</p>
<p>但是很遗憾，现在还不能进行任何调优，只能期待以后的版本了。但是，我们可以调节slab class的大小的差别。接下来说明growth factor选项。</p>
<h1>使用Growth Factor进行调优</h1>
<p>memcached在启动时指定 Growth Factor因子（通过-f选项），就可以在某种程度上控制slab之间的差异。默认值为1.25。但是，在该选项出现之前，这个因子曾经固定为2，称为&#8220;powers of 2&#8221;策略。</p>
<p>让我们用以前的设置，以verbose模式启动memcached试试看：</p><pre><code>$ memcached -f 2 -vv
</code></pre>
<p>下面是启动后的verbose输出：</p><pre><code>slab class   1: chunk size    128 perslab  8192
slab class   2: chunk size    256 perslab  4096
slab class   3: chunk size    512 perslab  2048
slab class   4: chunk size   1024 perslab  1024
slab class   5: chunk size   2048 perslab   512
slab class   6: chunk size   4096 perslab   256
slab class   7: chunk size   8192 perslab   128
slab class   8: chunk size  16384 perslab    64
slab class   9: chunk size  32768 perslab    32
slab class  10: chunk size  65536 perslab    16
slab class  11: chunk size 131072 perslab     8
slab class  12: chunk size 262144 perslab     4
slab class  13: chunk size 524288 perslab     2
</code></pre>
<p>可见，从128字节的组开始，组的大小依次增大为原来的2倍。这样设置的问题是，slab之间的差别比较大，有些情况下就相当浪费内存。因此，为尽量减少内存浪费，两年前追加了growth factor这个选项。</p>
<p>来看看现在的默认设置（f=1.25）时的输出（篇幅所限，这里只写到第10组）：</p><pre><code>slab class   1: chunk size     88 perslab 11915
slab class   2: chunk size    112 perslab  9362
slab class   3: chunk size    144 perslab  7281
slab class   4: chunk size    184 perslab  5698
slab class   5: chunk size    232 perslab  4519
slab class   6: chunk size    296 perslab  3542
slab class   7: chunk size    376 perslab  2788
slab class   8: chunk size    472 perslab  2221
slab class   9: chunk size    592 perslab  1771
slab class  10: chunk size    744 perslab  1409
</code></pre>
<p>可见，组间差距比因子为2时小得多，更适合缓存几百字节的记录。从上面的输出结果来看，可能会觉得有些计算误差，这些误差是为了保持字节数的对齐而故意设置的。</p>
<p>将memcached引入产品，或是直接使用默认值进行部署时，最好是重新计算一下数据的预期平均长度，调整growth factor，以获得最恰当的设置。内存是珍贵的资源，浪费就太可惜了。</p>
<p>接下来介绍一下如何使用memcached的stats命令查看slabs的利用率等各种各样的信息。</p>
<h1>查看memcached的内部状态</h1>
<p>memcached有个名为stats的命令，使用它可以获得各种各样的信息。执行命令的方法很多，用telnet最为简单：</p><pre><code>$ telnet 主机名 端口号
</code></pre>
<p>连接到memcached之后，输入stats再按回车，即可获得包括资源利用率在内的各种信息。此外，输入&#8221;stats slabs&#8221;或&#8221;stats items&#8221;还可以获得关于缓存记录的信息。结束程序请输入quit。</p>
<p>这些命令的详细信息可以参考memcached软件包内的protocol.txt文档。</p><pre><code>$ telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
stats
STAT pid 481
STAT uptime 16574
STAT time 1213687612
STAT version 1.2.5
STAT pointer_size 32
STAT rusage_user 0.102297
STAT rusage_system 0.214317
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 6
STAT total_connections 8
STAT connection_structures 7
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT evictions 0
STAT bytes_read 20
STAT bytes_written 465
STAT limit_maxbytes 67108864
STAT threads 4
END
quit
</code></pre>
<p>另外，如果安装了libmemcached这个面向C/C++语言的客户端库，就会安装 memstat 这个命令。使用方法很简单，可以用更少的步骤获得与telnet相同的信息，还能一次性从多台服务器获得信息。</p><pre><code>$ memstat --servers=server1,server2,server3,...
</code></pre>
<p>libmemcached可以从下面的地址获得：</p>
<ul><li>http://tangent.org/552/libmemcached.html </li></ul>
<h1>查看slabs的使用状况</h1>
<p>使用memcached的创造着Brad写的名为memcached-tool的Perl脚本，可以方便地获得slab的使用情况（它将memcached的返回值整理成容易阅读的格式）。可以从下面的地址获得脚本：</p>
<ul><li>http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool </li></ul>
<p>使用方法也极其简单：</p><pre><code>$ memcached-tool 主机名:端口 选项
</code></pre>
<p>查看slabs使用状况时无需指定选项，因此用下面的命令即可：</p><pre><code>$ memcached-tool 主机名:端口
</code></pre>
<p>获得的信息如下所示：</p><pre><code> #  Item_Size   Max_age  1MB_pages Count   Full?
 1     104 B  1394292 s    1215 12249628    yes
 2     136 B  1456795 s      52  400919     yes
 3     176 B  1339587 s      33  196567     yes
 4     224 B  1360926 s     109  510221     yes
 5     280 B  1570071 s      49  183452     yes
 6     352 B  1592051 s      77  229197     yes
 7     440 B  1517732 s      66  157183     yes
 8     552 B  1460821 s      62  117697     yes
 9     696 B  1521917 s     143  215308     yes
10     872 B  1695035 s     205  246162     yes
11     1.1 kB 1681650 s     233  221968     yes
12     1.3 kB 1603363 s     241  183621     yes
13     1.7 kB 1634218 s      94   57197     yes
14     2.1 kB 1695038 s      75   36488     yes
15     2.6 kB 1747075 s      65   25203     yes
16     3.3 kB 1760661 s      78   24167     yes
</code></pre>
<p>各列的含义为：</p>
<table>
<tbody>
<tr>
<td>列 </td>
<td>含义 </td></tr>
<tr>
<td># </td>
<td>slab class编号 </td></tr>
<tr>
<td>Item_Size </td>
<td>Chunk大小 </td></tr>
<tr>
<td>Max_age </td>
<td>LRU内最旧的记录的生存时间 </td></tr>
<tr>
<td>1MB_pages </td>
<td>分配给Slab的页数 </td></tr>
<tr>
<td>Count </td>
<td>Slab内的记录数 </td></tr>
<tr>
<td>Full? </td>
<td>Slab内是否含有空闲chunk </td></tr></tbody></table>
<p>从这个脚本获得的信息对于调优非常方便，强烈推荐使用。</p>
<h1>内存存储的总结</h1>
<p>本次简单说明了memcached的缓存机制和调优方法。希望读者能理解memcached的内存管理原理及其优缺点。</p>
<p>下次将继续说明LRU和Expire等原理，以及memcached的最新发展方向&#8212;&#8212; 可扩充体系（pluggable architecher））。</p></div><img src ="http://www.blogjava.net/hao446tian/aggbug/394792.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2013-01-26 11:49 <a href="http://www.blogjava.net/hao446tian/archive/2013/01/26/394792.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached完全剖析–1. memcached的基础(转)</title><link>http://www.blogjava.net/hao446tian/archive/2013/01/26/394791.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Sat, 26 Jan 2013 03:47:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2013/01/26/394791.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/394791.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2013/01/26/394791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/394791.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/394791.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>翻译一篇技术评论社的文章，是讲memcached的连载。<a href="http://www.fcicq.net/wp/">fcicq</a>同学说这个东西很有用，希望大家喜欢。</p>
<ul><li>发表日：2008/7/2</li><li>作者：长野雅广(Masahiro Nagano)</li><li>原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0001">http://gihyo.jp/dev/feature/01/memcached/0001</a> </li></ul>
<p>我是<a href="http://mixi.jp/">mixi株式会社</a>开发部系统运营组的长野。日常负责程序的运营。从今天开始，将分几次针对最近在Web应用的可扩展性领域的热门话题memcached，与我公司开发部研究开发组的前坂一起，说明其内部结构和使用。</p>
<p><span id="more-593"></span></p>
<h1>memcached是什么？</h1>
<p><a href="http://www.danga.com/memcached/">memcached</a> 是以<a href="http://www.livejournal.com/">LiveJournal</a> 旗下<a href="http://www.danga.com/">Danga Interactive</a> 公司的<a href="http://www.bradfitz.com/">Brad Fitzpatric</a> 为首开发的一款软件。现在已成为 <a href="http://mixi.jp/">mixi</a>、 <a href="http://www.hatena.ne.jp/">hatena</a>、 <a href="http://www.facebook.com/">Facebook</a>、 <a href="http://www.vox.com/">Vox</a>、LiveJournal等众多服务中提高Web应用扩展性的重要因素。</p>
<p>许多Web应用都将数据保存到RDBMS中，应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中，就会出现RDBMS的负担加重、数据库响应恶化、网站显示延迟等重大影响。</p>
<p>这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。一般的使用目的是，通过缓存数据库查询结果，减少数据库访问次数，以提高动态Web应用的速度、提高可扩展性。</p>
<p><img alt="memcached-0001-01.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0001-01.png" /></p>
<p>图1 一般情况下memcached的用途</p>
<h1>memcached的特征</h1>
<p>memcached作为高速运行的分布式缓存服务器，具有以下的特点。</p>
<ul><li>协议简单</li><li>基于libevent的事件处理</li><li>内置内存存储方式</li><li>memcached不互相通信的分布式 </li></ul>
<h2>协议简单</h2>
<p>memcached的服务器客户端通信并不使用复杂的XML等格式，而使用简单的基于文本行的协议。因此，通过telnet 也能在memcached上保存数据、取得数据。下面是例子。</p><pre><code>$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3     （保存命令）
bar               （数据）
STORED            （结果）
get foo           （取得命令）
VALUE foo 0 3     （数据）
bar               （数据）
</code></pre>
<p>协议文档位于memcached的源代码内，也可以参考以下的URL。</p>
<ul><li><a href="http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt">http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt</a> </li></ul>
<h2>基于libevent的事件处理</h2>
<p>libevent是个程序库，它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加，也能发挥O(1)的性能。 memcached使用这个libevent库，因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。关于事件处理这里就不再详细介绍，可以参考Dan Kegel的The C10K Problem。</p>
<ul><li><strong>libevent</strong>: <a href="http://www.monkey.org/~provos/libevent/">http://www.monkey.org/~provos/libevent/</a></li><li><strong>The C10K Problem</strong>: <a href="http://www.kegel.com/c10k.html">http://www.kegel.com/c10k.html</a> </li></ul>
<h2>内置内存存储方式</h2>
<p>为了提高性能，memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中，因此重启memcached、重启操作系统会导致全部数据消失。另外，内容容量达到指定值之后，就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器，因此并没有过多考虑数据的永久性问题。关于内存存储的详细信息，本连载的第二讲以后前坂会进行介绍，请届时参考。</p>
<h2>memcached不互相通信的分布式</h2>
<p>memcached尽管是&#8220;分布式&#8221;缓存服务器，但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么，怎样进行分布式呢？这完全取决于客户端的实现。本连载也将介绍memcached的分布式。</p>
<p><img alt="memcached-0001-02.png" src="http://tech.idv2.com/wp-content/uploads/2008/07/memcached-0001-02.png" /></p>
<p>图2 memcached的分布式</p>
<p>接下来简单介绍一下memcached的使用方法。</p>
<h1>安装memcached</h1>
<p>memcached的安装比较简单，这里稍加说明。</p>
<p>memcached支持许多平台。 * Linux * FreeBSD * Solaris (memcached 1.2.5以上版本) * Mac OS X</p>
<p>另外也能安装在Windows上。这里使用Fedora Core 8进行说明。</p>
<h2>memcached的安装</h2>
<p>运行memcached需要本文开头介绍的libevent库。Fedora 8中有现成的rpm包，通过yum命令安装即可。</p><pre><code>$ sudo yum install libevent libevent-devel
</code></pre>
<p>memcached的源代码可以从memcached网站上下载。本文执笔时的最新版本为1.2.5。 Fedora 8虽然也包含了memcached的rpm，但版本比较老。因为源代码安装并不困难，这里就不使用rpm了。</p>
<ul><li><strong>下载memcached</strong>：<a href="http://www.danga.com/memcached/download.bml">http://www.danga.com/memcached/download.bml</a> </li></ul>
<p>memcached安装与一般应用程序相同，configure、make、make install就行了。</p><pre><code>$ wget http://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz
$ tar zxf memcached-1.2.5.tar.gz
$ cd memcached-1.2.5
$ ./configure
$ make
$ sudo make install
</code></pre>
<p>默认情况下memcached安装到/usr/local/bin下。</p>
<h2>memcached的启动</h2>
<p>从终端输入以下命令，启动memcached。</p><pre><code>$ /usr/local/bin/memcached -p 11211 -m 64m -vv
slab class   1: chunk size     88 perslab 11915
slab class   2: chunk size    112 perslab  9362
slab class   3: chunk size    144 perslab  7281
中间省略
slab class  38: chunk size 391224 perslab     2
slab class  39: chunk size 489032 perslab     2
&lt;23 server listening
&lt;24 send buffer was 110592, now 268435456
&lt;24 server listening (udp)
&lt;24 server listening (udp)
&lt;24 server listening (udp)
&lt;24 server listening (udp)
</code></pre>
<p>这里显示了调试信息。这样就在前台启动了memcached，监听TCP端口11211 最大内存使用量为64M。调试信息的内容大部分是关于存储的信息，下次连载时具体说明。</p>
<p>作为daemon后台启动时，只需</p><pre><code>$ /usr/local/bin/memcached -p 11211 -m 64m -d
</code></pre>
<p>这里使用的memcached启动选项的内容如下。</p>
<table>
<tbody>
<tr>
<td>选项</td>
<td>说明 </td></tr>
<tr>
<td>-p </td>
<td>使用的TCP端口。默认为11211 </td></tr>
<tr>
<td>-m </td>
<td>最大内存大小。默认为64M </td></tr>
<tr>
<td>-vv </td>
<td>用very verbose模式启动，调试信息和错误输出到控制台 </td></tr>
<tr>
<td>-d </td>
<td>作为daemon在后台启动 </td></tr></tbody></table>
<p>上面四个是常用的启动选项，其他还有很多，通过</p><pre><code>$ /usr/local/bin/memcached -h
</code></pre>
<p>命令可以显示。许多选项可以改变memcached的各种行为，推荐读一读。</p>
<h1>用客户端连接</h1>
<p>许多语言都实现了连接memcached的客户端，其中以Perl、PHP为主。仅仅memcached网站上列出的语言就有</p>
<ul><li>Perl</li><li>PHP</li><li>Python</li><li>Ruby</li><li>C#</li><li>C/C++</li><li>Lua </li></ul>
<p>等等。</p>
<ul><li><strong>memcached客户端API</strong>：<a href="http://www.danga.com/memcached/apis.bml">http://www.danga.com/memcached/apis.bml</a> </li></ul>
<p>这里介绍通过mixi正在使用的Perl库链接memcached的方法。</p>
<h1>使用Cache::Memcached</h1>
<p>Perl的memcached客户端有</p>
<ul><li>Cache::Memcached</li><li>Cache::Memcached::Fast</li><li>Cache::Memcached::libmemcached </li></ul>
<p>等几个CPAN模块。这里介绍的Cache::Memcached是memcached的作者Brad Fitzpatric的作品，应该算是memcached的客户端中应用最为广泛的模块了。</p>
<ul><li><strong>Cache::Memcached &#8211; search.cpan.org</strong>: <a href="http://search.cpan.org/dist/Cache-Memcached/">http://search.cpan.org/dist/Cache-Memcached/</a> </li></ul>
<h2>使用Cache::Memcached连接memcached</h2>
<p>下面的源代码为通过Cache::Memcached连接刚才启动的memcached的例子。</p><pre><code>#!/usr/bin/perl

use strict;
use warnings;
use Cache::Memcached;

my $key = "foo";
my $value = "bar";
my $expires = 3600; # 1 hour
my $memcached = Cache::Memcached-&gt;new({
    servers =&gt; ["127.0.0.1:11211"],
    compress_threshold =&gt; 10_000
});

$memcached-&gt;add($key, $value, $expires);
my $ret = $memcached-&gt;get($key);
print "$ret\n";
</code></pre>
<p>在这里，为Cache::Memcached指定了memcached服务器的IP地址和一个选项，以生成实例。 Cache::Memcached常用的选项如下所示。</p>
<table>
<tbody>
<tr>
<td>选项 </td>
<td>说明 </td></tr>
<tr>
<td>servers </td>
<td>用数组指定memcached服务器和端口 </td></tr>
<tr>
<td>compress_threshold </td>
<td>数据压缩时使用的值 </td></tr>
<tr>
<td>namespace </td>
<td>指定添加到键的前缀 </td></tr></tbody></table>
<p>另外，Cache::Memcached通过Storable模块可以将Perl的复杂数据序列化之后再保存，因此散列、数组、对象等都可以直接保存到memcached中。</p>
<h2>保存数据</h2>
<p>向memcached保存数据的方法有</p>
<ul><li>add</li><li>replace</li><li>set </li></ul>
<p>它们的使用方法都相同：</p><pre><code>my $add = $memcached-&gt;add( '键', '值', '期限' );
my $replace = $memcached-&gt;replace( '键', '值', '期限' );
my $set = $memcached-&gt;set( '键', '值', '期限' );
</code></pre>
<p>向memcached保存数据时可以指定期限(秒)。不指定期限时，memcached按照LRU算法保存数据。这三个方法的区别如下：</p>
<table>
<tbody>
<tr>
<td>选项 </td>
<td>说明 </td></tr>
<tr>
<td>add </td>
<td>仅当存储空间中不存在键相同的数据时才保存 </td></tr>
<tr>
<td>replace </td>
<td>仅当存储空间中存在键相同的数据时才保存 </td></tr>
<tr>
<td>set </td>
<td>与add和replace不同，无论何时都保存 </td></tr></tbody></table>
<h2>获取数据</h2>
<p>获取数据可以使用get和get_multi方法。</p><pre><code>my $val = $memcached-&gt;get('键');
my $val = $memcached-&gt;get_multi('键1', '键2', '键3', '键4', '键5');
</code></pre>
<p>一次取得多条数据时使用get<em>multi。get</em>multi可以非同步地同时取得多个键值，其速度要比循环调用get快数十倍。</p>
<h2>删除数据</h2>
<p>删除数据使用delete方法，不过它有个独特的功能。</p><pre><code>$memcached-&gt;delete('键', '阻塞时间(秒)');
</code></pre>
<p>删除第一个参数指定的键的数据。第二个参数指定一个时间值，可以禁止使用同样的键保存新数据。此功能可以用于防止缓存数据的不完整。但是要注意，<strong>set函数忽视该阻塞，照常保存数据</strong></p>
<h2>增一和减一操作</h2>
<p>可以将memcached上特定的键值作为计数器使用。</p><pre><code>my $ret = $memcached-&gt;incr('键');
$memcached-&gt;add('键', 0) unless defined $ret;
</code></pre>
<p>增一和减一是原子操作，但未设置初始值时，不会自动赋成0。因此，应当进行错误检查，必要时加入初始化操作。而且，服务器端也不会对超过2 SUP(32)时的行为进行检查。</p>
<h1>总结</h1>
<p>这次简单介绍了memcached，以及它的安装方法、Perl客户端Cache::Memcached的用法。只要知道，memcached的使用方法十分简单就足够了。</p>
<p>下次由前坂来说明memcached的内部结构。了解memcached的内部构造，就能知道如何使用memcached才能使Web应用的速度更上一层楼。欢迎继续阅读下一章。</p></div><!-- .entry-content --> <img src ="http://www.blogjava.net/hao446tian/aggbug/394791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2013-01-26 11:47 <a href="http://www.blogjava.net/hao446tian/archive/2013/01/26/394791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>查看、分析memcached使用状态</title><link>http://www.blogjava.net/hao446tian/archive/2012/04/11/373773.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Wed, 11 Apr 2012 03:03:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2012/04/11/373773.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/373773.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2012/04/11/373773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/373773.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/373773.html</trackback:ping><description><![CDATA[访问量上升，数据库压力大，怎么办？好办法是在中间挡一层缓存！这个缓存要求高效，不能比数据库慢，否则服务质量受影响；如果能把数据用hash打散存储到硬盘，也是可以的，不过在内存越来越便宜的今天，还是使用内存吧！ 
<p>&nbsp;&nbsp;&nbsp; mysql也有自己的缓存，也是存储在内存的，但是有一个说法是：</p>
<p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#f3f3f3">
<p><font style="font-weight: bold; color: #990000">以下是引用片段：</font></p>
<p>只能有一个实例<br />意味着你能存储内容的上限就是你服务器的可用内存，一台服务器能有多少内存？你又能存多少呢？ </p>
<p>只要有写操作，mysql的query cache就失效<br />只要数据库内容稍有改变，那怕改变的是其他行，mysql的query cache也会失效</p></td></tr></tbody></table></p>
<p>&nbsp;&nbsp;&nbsp; 再说，如果mysql都抗不住了，怎么还能指望它提供的缓存呢？</p>
<p>&nbsp;&nbsp;&nbsp; 所以我可以使用memcached了！他的好处和如何用可以参考：</p>
<p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#f3f3f3"><font style="font-weight: bold; color: #990000">以下是引用片段：</font><br />
<p>1：《<a href="http://www.ourmysql.com/archives/415">Memcache和mysql交互流程操作原理</a>》</p>
<p>2：《<a href="http://www.ourmysql.com/archives/195">让memcached和mysql更好的工作</a>》</p></td></tr></tbody></table></p>
<p>&nbsp;&nbsp;&nbsp; 开发时面对需求是个麻烦事，更漫长而闹心的是维护，所以我更关心的是memcached运行中的情况。还好的是，memcached的作者给我们提供查看运行情况的命令。主要是&#8220;stats&#8221;，使用方法为 &#8220;telnet ip 端口号&#8221;，登录后使用&#8220;stats&#8221;命令。</p>
<p>&nbsp;&nbsp;&nbsp; 然后你可以看见很多内容，具体可以参考：《<a href="http://lists.danga.com/pipermail/memcached/2006-March/002065.html">memcacche stats</a>》</p>
<p>
<table style="border-right: #cccccc 1px dotted; table-layout: fixed; border-top: #cccccc 1px dotted; border-left: #cccccc 1px dotted; border-bottom: #cccccc 1px dotted" cellspacing="0" cellpadding="6" width="95%" align="center" border="0">
<tbody>
<tr>
<td style="word-wrap: break-word" bgcolor="#f3f3f3">
<p><font style="font-weight: bold; color: #990000">以下是引用片段：</font></p>
<p>pid = process id<br />uptime = number of seconds since the process was started<br />time = current time<br />version = memcached version<br />rusage_user = seconds the cpu has devoted to the process as the user<br />rusage_system = seconds the cpu has devoted to the process as the system<br />curr_items = total number of items currently in memcache<br />total_items = total number of items that have passed through the cache<br />bytes = total number of bytes currently in use by curr_items<br />curr_connections = total number of open connections to memcached<br />connection_structures = ???<br />cmd_get = total GET commands issued to the server<br />cmd_set = total SET commands issued to the server<br />get_hits = total number of times a GET command was able to retrieve and<br />return data<br />get_misses = total number of times a GET command was unable to retrieve and<br />return data<br />bytes_read = total number of bytes input into the server<br />bytes_written = total number of bytes written by the server<br />limit_maxbytes = total storage bytes available to the server.</p></td></tr></tbody></table></p>
<p>&nbsp;&nbsp;&nbsp; 着重说一下几个对观测很有用的项。</p>
<p>&nbsp;&nbsp;&nbsp; <strong>limit_maxbytes、bytes</strong></p>
<p>&nbsp;&nbsp;&nbsp; memcached在存储的时候是可以设置失效时间的，但如果存储已经满了，那旧数据即使没有到过期时间，也会被移除。所以需要观察memcached存储是否已经满了，同时这对扩容也是有意义的参考。limit_maxbytes即总的存储大小，而bytes就是已经使用的大小，从这两个数据就可以看出在memcached启动时，我们为它分配的内存是否足够使用。</p>
<p>&nbsp;&nbsp;&nbsp; <strong>cmd_get、cmd_set</strong></p>
<p>&nbsp;&nbsp;&nbsp; memcached启动后，我们对它一共做了多少次读取操作呢？从这两个参数可以观察出来。</p>
<p>&nbsp;&nbsp;&nbsp; <strong>get_hits、get_misses</strong></p>
<p>&nbsp;&nbsp;&nbsp; 使用memcached后，我们需要评估我们使用的策略是否合理。不能够使用中间缓存后，后端的数据库还是有较大的访问量，这样的话中间缓存就变得没有意义了。get_hits表示命中了多少次读取，即来memcached取到了多少有效数据；get_misses表示没有命中的次数，即此次来取数据的时候，memcached并没有你所查询的数据。如果没有清零统计数据的话，cmd_get = get_hits + get_misses。</p>
<p>&nbsp;&nbsp;&nbsp; memcached 的状态查询还有其它的命令，可以参考：《<a href="http://blog.alwaysmylove.net/2008/04/10/stats-command-in-memcached/">Memcached的stats命令</a>》</p>
<p>&nbsp;&nbsp;&nbsp; 如下：</p>
<p>stats reset<br />清空统计数据</p>
<p>stats malloc<br />显示内存分配数据</p>
<p>stats maps<br />这个不太确定，看源代码是把/proc/self/maps的数据显示出来。</p>
<p>stats cachedump slab_id limit_num<br />显示某个slab中的前limit_num个key列表，显示格式如下<br />ITEM key_name [ value_length b; expire_time|access_time s]<br />其中，memcached 1.2.2及以前版本显示的是&nbsp; 访问时间(timestamp)<br />1.2.4以上版本，包括1.2.4显示 过期时间(timestamp)<br />如果是永不过期的key，expire_time会显示为服务器启动的时间</p>
<p>stats cachedump 7 2<br />ITEM copy_test1 [250 b; 1207795754 s]<br />ITEM copy_test [248 b; 1207793649 s]</p>
<p>stats slabs<br />显示各个slab的信息，包括chunk的大小、数目、使用情况等</p>
<p>stats items<br />显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)</p>
<p>stats detail [on|off|dump]<br />设置或者显示详细操作记录</p>
<p>参数为on，打开详细操作记录<br />参数为off，关闭详细操作记录<br />参数为dump，显示详细操作记录(每一个键值get、set、hit、del的次数)</p>
<p>stats detail dump<br />PREFIX copy_test2 get 1 hit 1 set 0 del 0<br />PREFIX copy_test1 get 1 hit 1 set 0 del 0<br />PREFIX cpy get 1 hit 0 set 0 del 0<br /></p>
<div class="entry-content">
<p>memcached数据存储和取回相关的基本命令只有4条。<br />下面将采用telnet与memcached进行交互，并介绍这4条基本命令。<br />假设memcached服务器在本机上，并监听在默认端口11211上。</p>
<p>telnet连接到memcached：<br />telnet 127.0.0.1 11211</p>
<p><strong>SET：添加一个新的条目到memcached，或是用新的数据替换掉已存在的条目</strong></p>
<p>set test1 0 0 10<br />testing001<br />STORED</p>
<p><strong>ADD：仅当key不存在的情况下存储数据。如果一个key已经存在，将得到NOT_STORED的响应</strong></p>
<p>add test1 0 0 10<br />testing002<br />NOT_STORED<br />add test2 0 0 10<br />testing002<br />STORED</p>
<p><strong>REPLACE：仅当key已经存在的情况下存储数据。如果一个key不存在，将得到NOT_STORED的响应</strong></p>
<p>replace test1 0 0 10<br />testing003<br />STORED<br />replace test3 0 0 10<br />testing003<br />NOT_STORED</p>
<p><strong>GET：从memcached中返回数据。从缓存中返回数据时，将在第一行得到key的名字，flag的值和返回的value的长度。真正的数据在第二行，最后返回END。如果key并不存在，那么在第一行就直接返回END。</strong></p>
<p>get test1<br />VALUE test1 0 10<br />testing003<br />END<br />get test4<br />END<br />get test1 test2<br />VALUE test1 0 10<br />testing003<br />END</p>
<p>注：像上面那样你可以在一个请求中包含多个由空格分开的key。当请求多个key时，将只会得到那些有存储数据的key的响应。memcached将不会响应没有存储Data的key。<br /></p></div>
<p>###################################################################<br /></p>
<p>1、启动Memcache 常用参数<br />memcached 1.4.3<br />-p &lt;num&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 设置端口号(默认不设置为: 11211)<br />-U &lt;num&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UDP监听端口 (默认: 11211, 0 时关闭) &nbsp;<span id="more-273"></span><br />-l &lt;ip_addr&gt;&nbsp; 绑定地址 (默认:所有都允许,无论内外网或者本机更换IP，有安全隐患，若设置为127.0.0.1就只能本机访问)<br />-d&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 独立进程运行<br />-u &lt;username&gt; 绑定使用指定用于运行进程 &lt;username&gt;<br />-m &lt;num&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 允许最大内存用量，单位M (默认: 64 MB)<br />-P &lt;file&gt;&nbsp;&nbsp;&nbsp;&nbsp; 将PID写入文件&lt;file&gt;，这样可以使得后边进行快速进程终止, 需要与 -d 一起使用<br />如：<br />在linux下：./usr/local/bin/memcached -d -u jb-mc -l 192.168.1.197 -m 2048 -p 12121<br />在window下：d:\App_Serv\memcached\memcached.exe -d RunService -l 127.0.0.1 -p 11211 -m 500<br />在windows下注册为服务后运行：<br />sc.exe create jb-Memcached binpath= &#8220;d:\App_Serv\memcached\memcached.exe -d RunService -p 11211 -m 500&#8243; start= auto<br />net start jb-Memcached</p>
<p>2、连接：telnet 127.0.0.1 11211<br />不要说不会用这个？</p>
<p>3、写入memcache<br />&lt;command name&gt; &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt;\r\n &lt;data block&gt;\r\n<br />a) &lt;command name&gt; 可以是&#8221;set&#8221;, &#8220;add&#8221;, &#8220;replace&#8221;。<br />&#8220;set&#8221;表示按照相应的&lt;key&gt;存储该数据，没有的时候增加，有的覆盖。<br />&#8220;add&#8221;表示按照相应的&lt;key&gt;添加该数据,但是如果该&lt;key&gt;已经存在则会操作失败。<br />&#8220;replace&#8221;表示按照相应的&lt;key&gt;替换数据,但是如果该&lt;key&gt;不存在则操作失败</p>
<p>b) &lt;key&gt; 客户端需要保存数据的key。</p>
<p>c) &lt;flags&gt; 是一个16位的无符号的整数(以十进制的方式表示)。<br />该标志将和需要存储的数据一起存储,并在客户端get数据时返回。<br />客户可以将此标志用做特殊用途，此标志对服务器来说是不透明的。</p>
<p>d) &lt;exptime&gt; 过期的时间。<br />若为0表示存储的数据永远不过时(但可被服务器算法：LRU 等替换)。<br />如果非0(unix时间或者距离此时的秒数),当过期后,服务器可以保证用户得不到该数据(以服务器时间为标准)。</p>
<p>e) &lt;bytes&gt; 需要存储的字节数(不包含最后的&#8221;\r\n&#8221;),当用户希望存储空数据时,&lt;bytes&gt;可以为0</p>
<p>f) 最后客户端需要加上&#8221;\r\n&#8221;作为&#8221;命令头&#8221;的结束标志。<br />&lt;data block&gt;\r\n</p>
<p>紧接着&#8221;命令头&#8221;结束之后就要发送数据块(即希望存储的数据内容),最后加上&#8221;\r\n&#8221;作为此次通讯的结束。</p>
<p>结果响应：reply<br />当以上数据发送结束之后,服务器将返回一个应答。可能有如下的情况:</p>
<p>a) &#8220;STORED\r\n&#8221;：表示存储成功<br />b) &#8220;NOT_STORED\r\n&#8221; ： 表示存储失败,但是该失败不是由于错误。<br />通常这是由于&#8221;add&#8221;或者&#8221;replace&#8221;命令本身的要求所引起的,或者该项在删除队列之中。</p>
<p>如： set key 33 0 4\r\n<br />ffff\r\n</p>
<p>4、获取/检查KeyValue<br />get &lt;key&gt;*\r\n<br />a) &lt;key&gt;* 表示一个或者多个key(以空格分开)<br />b) &#8220;\r\n&#8221; 命令头的结束</p>
<p>结果响应：reply<br />服务器端将返回0个或者多个的数据项。每个数据项都是由一个文本行和一个数据块组成。当所有的数据项都接收完毕将收到&#8221;END\r\n&#8221;<br />每一项的数据结构：<br />VALUE &lt;key&gt; &lt;flags&gt; &lt;bytes&gt;\r\n<br />&lt;data block&gt;\r\n</p>
<p>a) &lt;key&gt; 希望得到存储数据的key<br />b) &lt;falg&gt; 发送set命令时设置的标志项<br />c) &lt;bytes&gt; 发送数据块的长度(不包含&#8221;\r\n&#8221;)<br />d) &#8220;\r\n&#8221; 文本行的结束标志<br />e) &lt;data block&gt; 希望接收的数据项。<br />f) &#8220;\r\n&#8221; 接收一个数据项的结束标志。</p>
<p>如果有些key出现在get命令行中但是没有返回相应的数据，这意味着服务器中不存在这些项，这些项过时了，或者被删除了<br />如：get aa<br />VALUE aa 33 4<br />ffff<br />END</p>
<p>5、删除KeyValue：<br />delete &lt;key&gt; &lt;time&gt;\r\n</p>
<p>a) &lt;key&gt; 需要被删除数据的key<br />b) &lt;time&gt; 客户端希望服务器将该数据删除的时间(unix时间或者从现在开始的秒数)<br />c) &#8220;\r\n&#8221; 命令头的结束</p>
<p>6、检查Memcache服务器状态：<br />stats\r\n<br />在这里可以看到memcache的获取次数，当前连接数，写入次数，已经命中率等；</p>
<p>pid ： 进程id<br />uptime ：总的运行时间，秒数<br />time ： 当前时间<br />version ： 版本号<br />&#8230;&#8230;<br />curr_items ： 当前缓存中的KeyValue数量<br />total_items ： 曾经总共经过缓存的KeyValue数量<br />bytes ： 所有的缓存使用的内存量<br />curr_connections 当前连接数<br />&#8230;.<br />cmd_get ： 总获取次数<br />cmd_set ： 总的写入次数<br />get_hits ： 总的命中次数<br />miss_hits :&nbsp; 获取失败次数<br />&#8230;..<br />bytes_read ： 总共读取的流量字节数<br />bytes_written ： 总的写入流量字节<br />limit_maxbytes ： 最大允许使用的内存量，字节</p>
<p>7、高级缓存细节查看方法：<br />stats reset<br />清空统计数据</p>
<p>stats malloc<br />显示内存分配数据</p>
<p>stats cachedump slab_id limit_num<br />显示某个slab中的前limit_num个key列表，显示格式如下<br />ITEM key_name [ value_length b; expire_time|access_time s]<br />其中，memcached 1.2.2及以前版本显示的是&nbsp; 访问时间(timestamp)<br />1.2.4以上版本，包括1.2.4显示 过期时间(timestamp)<br />如果是永不过期的key，expire_time会显示为服务器启动的时间</p>
<p>stats cachedump 7 2<br />ITEM copy_test1 [250 b; 1207795754 s]<br />ITEM copy_test [248 b; 1207793649 s]</p>
<p>stats slabs<br />显示各个slab的信息，包括chunk的大小、数目、使用情况等</p>
<p>stats items<br />显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)</p>
<p>stats detail [on|off|dump]<br />设置或者显示详细操作记录</p>
<p>参数为on，打开详细操作记录<br />参数为off，关闭详细操作记录<br />参数为dump，显示详细操作记录(每一个键值get、set、hit、del的次数)</p>
<p>8、清空所有键值<br />flush_all<br />注：flush并不会将items删除，只是将所有的items标记为expired，因此这时memcache依旧占用所有内存。</p>
<p>8、退出<br />quit\r\n</p>
<p>&nbsp;</p><img src ="http://www.blogjava.net/hao446tian/aggbug/373773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2012-04-11 11:03 <a href="http://www.blogjava.net/hao446tian/archive/2012/04/11/373773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用java5的注解和Sping/AspectJ的AOP 来实现Memcached的缓存</title><link>http://www.blogjava.net/hao446tian/archive/2012/04/10/373725.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Tue, 10 Apr 2012 09:18:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2012/04/10/373725.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/373725.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2012/04/10/373725.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/373725.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/373725.html</trackback:ping><description><![CDATA[<p>今天要介绍的是Simple-Spring-Memcached，它封装了对MemCached的调用，使MemCached的客户端开发变得超乎寻常的简单，只要一行代码就行：</p>
<p>@ReadThroughAssignCache(assignedKey = "VETS", expiration = 300, namespace = "NELZ")</p>
<p>是不是很神奇？这行代码指定了MemCached的key，过期时间和命名空间。假设你的MemCached服务器IP是：196.168.10.101，端口是：12000，那么在数据调用的配置文件中只要加上下面配置代码就可以了：</p>
<p>&nbsp;</p>
<div class="cnblogs_code"><pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080"> 1</span> <span style="color: #0000ff">&lt;</span><span style="color: #800000">import </span><span style="color: #ff0000">resource</span><span style="color: #0000ff">="classpath:simplesm-context.xml"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 5</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="memcachedConnectionBean"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.config.MemcachedConnectionBean"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 7</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="consistentHashing"</span><span style="color: #ff0000"> value</span><span style="color: #0000ff">="true"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 9</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="nodeList"</span><span style="color: #ff0000"> value</span><span style="color: #0000ff">="196.168.10.101:12000"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;</span><span style="color: #000000"><br /></span><span style="color: #008080"> 5</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;</span></div></pre></div>
<p>&nbsp;&nbsp;</p>
<p>从simplesm-context.xml的内容中，可以看出它所封装的类和方法：</p>
<p>&nbsp;</p>
<div class="cnblogs_code"><pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080">  1</span>   <span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="memcachedClientFactory"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.config.MemcachedClientFactory"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080">  3</span> <span style="color: #000000">    </span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="bean"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedConnectionBean"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080">  5</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080">  9</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> factory-bean</span><span style="color: #0000ff">="memcachedClientFactory"</span><span style="color: #ff0000"> factory-method</span><span style="color: #0000ff">="createMemcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 13</span> <span style="color: #000000">   </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.CacheKeyMethodStoreImpl"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 17</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.impl.DefaultKeyProvider"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 19</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 21</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 25</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="readThroughSingleCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.ReadThroughSingleCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 27</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 29</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 31</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 33</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 35</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="readThroughMultiCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.ReadThroughMultiCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 37</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 39</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 41</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 43</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 45</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="readThroughAssignCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.ReadThroughAssignCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 47</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 49</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 51</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 53</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br /></span><span style="color: #008080"> 54</span> <span style="color: #000000"><br /></span><span style="color: #008080"> 55</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="updateSingleCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.UpdateSingleCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 57</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 59</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 61</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 63</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;</span><span style="color: #000000"><br /></span><span style="color: #008080"> 64</span> <span style="color: #000000"><br /></span><span style="color: #008080"> 65</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="updateMultiCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.UpdateMultiCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 67</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 69</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 71</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 73</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 75</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="updateAssignCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.UpdateAssignCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 77</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 79</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 81</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 83</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 85</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="invalidateSingleCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.InvalidateSingleCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 87</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 89</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 91</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 93</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 95</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="invalidateMultiCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.InvalidateMultiCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080"> 97</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080"> 99</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080">101</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080">103</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /><br /><br /><br /><br /><br /><br /></span><span style="color: #008080">105</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">bean </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="invalidateAssignCache"</span><span style="color: #ff0000"> class</span><span style="color: #0000ff">="net.nelz.simplesm.aop.InvalidateAssignCacheAdvice"</span><span style="color: #0000ff">&gt;<br /></span><span style="color: #008080">107</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="cache"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="memcachedClient"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080">109</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="methodStore"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080">111</span> <span style="color: #000000">    </span><span style="color: #0000ff">&lt;</span><span style="color: #800000">property </span><span style="color: #ff0000">name</span><span style="color: #0000ff">="defaultKeyProvider"</span><span style="color: #ff0000"> ref</span><span style="color: #0000ff">="net.nelz.simplesm.DefaultKeyProvider"</span><span style="color: #ff0000"> </span><span style="color: #0000ff">/&gt;<br /></span><span style="color: #008080">113</span> <span style="color: #000000">  </span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">bean</span><span style="color: #0000ff">&gt;<br /></span></div></pre></div>
<p>&nbsp;</p>
<p>Simple-Spring-Memcached还提供了一个例子，在spring的petClinic例子中加入了几行代码，就实现了对MemCached的调用：</p>
<p>&nbsp;</p>
<div class="cnblogs_code"><pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080"> 1</span> <span style="color: #0000ff">import</span><span style="color: #000000"> net.nelz.simplesm.annotations.ReadThroughAssignCache;<br /></span><span style="color: #008080"> 2</span> <span style="color: #000000"><br /></span><span style="color: #008080"> 3</span> <span style="color: #0000ff">import</span><span style="color: #000000"> net.nelz.simplesm.annotations.ReadThroughSingleCache;<br /></span><span style="color: #008080"> 4</span> <span style="color: #000000"><br /></span><span style="color: #008080"> 5</span> <span style="color: #000000">@ReadThroughAssignCache(assignedKey </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">VETS</span><span style="color: #000000">"</span><span style="color: #000000">, expiration </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">300</span><span style="color: #000000">, namespace </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">NELZ</span><span style="color: #000000">"</span><span style="color: #000000">)<br /></span><span style="color: #008080"> 6</span> <span style="color: #000000"><br /></span><span style="color: #008080"> 7</span> <span style="color: #000000">    </span><span style="color: #0000ff">public</span><span style="color: #000000"> Collection</span><span style="color: #000000">&lt;</span><span style="color: #000000">Vet</span><span style="color: #000000">&gt;</span><span style="color: #000000"> getVets() {<br /></span><span style="color: #008080"> 8</span> <span style="color: #000000"><br /></span><span style="color: #008080"> 9</span> <span style="color: #000000">        System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">\n ! ! !Gonna wait a bit: </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> Date() </span><span style="color: #000000">+</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">\n</span><span style="color: #000000">"</span><span style="color: #000000">);        <br /></span><span style="color: #008080">10</span> <span style="color: #000000"><br /></span><span style="color: #008080">11</span> <span style="color: #000000">        </span><span style="color: #0000ff">try</span><span style="color: #000000"> {<br /></span><span style="color: #008080">12</span> <span style="color: #000000"><br /></span><span style="color: #008080">13</span> <span style="color: #000000">            Thread.sleep(</span><span style="color: #000000">4000</span><span style="color: #000000">);<br /></span><span style="color: #008080">14</span> <span style="color: #000000"><br /></span><span style="color: #008080">15</span> <span style="color: #000000">        } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception ex) {}<br /></span><span style="color: #008080">16</span> <span style="color: #000000"><br /></span><span style="color: #008080">17</span> <span style="color: #000000">        </span><span style="color: #0000ff">return</span><span style="color: #000000"> sessionFactory.getCurrentSession().createQuery(</span><span style="color: #000000">"</span><span style="color: #000000">from Vet vet order by vet.lastName, vet.firstName</span><span style="color: #000000">"</span><span style="color: #000000">).list();<br /></span><span style="color: #008080">18</span> <span style="color: #000000"><br /></span><span style="color: #008080">19</span> <span style="color: #000000">         }<br /></span></div></pre></div>
<p>&nbsp;&nbsp;</p>
<p>为了加强测试的效果，在第一次读取数据时，故意停顿了一下(sleep)。</p><img src ="http://www.blogjava.net/hao446tian/aggbug/373725.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2012-04-10 17:18 <a href="http://www.blogjava.net/hao446tian/archive/2012/04/10/373725.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Memcached入门</title><link>http://www.blogjava.net/hao446tian/archive/2012/04/10/373723.html</link><dc:creator>昊天</dc:creator><author>昊天</author><pubDate>Tue, 10 Apr 2012 08:57:00 GMT</pubDate><guid>http://www.blogjava.net/hao446tian/archive/2012/04/10/373723.html</guid><wfw:comment>http://www.blogjava.net/hao446tian/comments/373723.html</wfw:comment><comments>http://www.blogjava.net/hao446tian/archive/2012/04/10/373723.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hao446tian/comments/commentRss/373723.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hao446tian/services/trackbacks/373723.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; <font color="#993300">Memcached是高性能的，分布式的内存对象缓存系统，用于在动态应用中减少数据库负载，提升访问速度。Memcached由Danga Interactive开发，用于提升LiveJournal.com访问速度的。LJ每秒动态页面访问量几千次，用户700万。Memcached将数据库负载大幅度降低，更好的分配资源，更快速访问。</font></p>
<p>&nbsp;&nbsp;&nbsp; 上网baidu了很多东西，几乎都差不多，而且基于java的说的很少，所有只有在研究了各个其他语言类的应用后再来尝试在java上进行简单的操作应用。先从memcached上进行说明，memcached的最新版是采用c语言进行开发和设计的，据说旧版的是采用perl语言开发的，而且它是一个应用软件来的，是作为缓存服务器的服务器端运行在服务器上的，需要使用特定的语言编写客户端与其进行通信来进行数据的缓存和获取。通常我们是把memcached安装运行在web服务器上，然后通过对需要的数据进行缓存，据我目前所知，所有数据的缓存设置和存取操作，以及数据的更新后替换操作全部需要程序来进行，而不是自动进行的（自动不知道能不能成功，呵呵）。下面从一个实际的例子来应用memcached。<br /></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先到<a href="http://splinedancer.com/memcached-win32/">http://splinedancer.com/memcached-win32/</a>下载windows版本的memcached，然后依次执行下面：<br />1、Unzip the binaries in your desired directory (eg. c:\memcached) ，<br />解压<br />2、Install the service using the command: 'c:\memcached\memcached.exe -d install' from the command line <br />cmd 通过 c:\memcached\memcached.exe -d install 进行安装&nbsp;<br />3、Start the server from the Microsoft Management Console or by running the following command: 'c:\memcached\memcached.exe -d start' <br />cmd 通过 c:\memcached\memcached.exe -d install 启用服务<br />4、Use the server, by default listening to port 11211&nbsp;<br />默认的端口为11211。<br />&nbsp;&nbsp;&nbsp; 执行完毕后，我们就可以在任务管理器中见到memcached.exe这个进程了。好了，我们的服务器已经正常运行了， 下面我们就来写java的客户端连接程序。到</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://github.com/gwhalin/Memcached-Java-Client/downloads">https://github.com/gwhalin/Memcached-Java-Client/downloads</a>下载memcahce客户端。<br />&nbsp;&nbsp;&nbsp;&nbsp;然后我们来编写代码，比如我提供的一个应用类如下：</p>
<p><span style="color: #0000ff">package</span><span style="color: #000000"> utils.cache;<br /></span><span style="color: #0000ff">import</span><span style="color: #000000"> java.util.Date;<br /></span><span style="color: #0000ff">import </span><span style="color: #000000">com.meetup.memcached.SockIOPool;</span><span style="color: #0000ff"><br />import </span><span style="color: #000000">com.meetup.memcached.MemcachedClient;</span><span style="color: #000000"><br /><br /></span><span style="color: #008000">/**</span><span style="color: #008000"><br />* 使用memcached的缓存实用类.<br />* <br />* </span><span style="color: #808080">@author</span><span style="color: #008000"> hao446tian<br />*<br /></span><span style="color: #008000">*/</span><span style="color: #000000"><br /></span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> MemCached<br />{<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 创建全局的唯一实例</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">protected</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> MemcachedClient mcc </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> MemcachedClient();<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">protected</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> MemCached memCached </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> MemCached();<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 设置与缓存服务器的连接池</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">static</span><span style="color: #000000"> {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 服务器列表和其权重</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String[] servers </span><span style="color: #000000">=</span><span style="color: #000000"> {</span><span style="color: #000000">"</span><span style="color: #000000">127.0.0.1:11211</span><span style="color: #000000">"</span><span style="color: #000000">};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Integer[] weights </span><span style="color: #000000">=</span><span style="color: #000000"> {</span><span style="color: #000000">3</span><span style="color: #000000">};<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 获取socke连接池的实例对象</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SockIOPool pool </span><span style="color: #000000">=</span><span style="color: #000000"> SockIOPool.getInstance();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 设置服务器信息</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setServers( servers );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setWeights( weights );<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 设置初始连接数、最小和最大连接数以及最大处理时间</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setInitConn( </span><span style="color: #000000">5</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setMinConn( </span><span style="color: #000000">5</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setMaxConn( </span><span style="color: #000000">250</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setMaxIdle( </span><span style="color: #000000">1000</span><span style="color: #000000"> </span><span style="color: #000000">*</span><span style="color: #000000"> </span><span style="color: #000000">60</span><span style="color: #000000"> </span><span style="color: #000000">*</span><span style="color: #000000"> </span><span style="color: #000000">60</span><span style="color: #000000"> </span><span style="color: #000000">*</span><span style="color: #000000"> </span><span style="color: #000000">6</span><span style="color: #000000"> );<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 设置主线程的睡眠时间</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setMaintSleep( </span><span style="color: #000000">30</span><span style="color: #000000"> );<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 设置TCP的参数，连接超时等</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setNagle( </span><span style="color: #0000ff">false</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setSocketTO( </span><span style="color: #000000">3000</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.setSocketConnectTO( </span><span style="color: #000000">0</span><span style="color: #000000"> );<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 初始化连接池</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pool.initialize();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">//</span><span style="color: #008000"> 压缩设置，超过指定大小（单位为K）的数据都会被压缩</span><span style="color: #008000"><br /></span><span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mcc.setCompressEnable( </span><span style="color: #0000ff">true</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mcc.setCompressThreshold( </span><span style="color: #000000">64</span><span style="color: #000000"> </span><span style="color: #000000">*</span><span style="color: #000000"> </span><span style="color: #000000">1024</span><span style="color: #000000"> );<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">/**</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; * 保护型构造方法，不允许实例化！<br />&nbsp;&nbsp;&nbsp;&nbsp; *<br />&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">*/</span><span style="color: #000000"><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">protected</span><span style="color: #000000"> MemCached()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">/**</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; * 获取唯一实例.<br />&nbsp;&nbsp;&nbsp;&nbsp; * </span><span style="color: #808080">@return</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">*/</span><span style="color: #000000"><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> MemCached getInstance()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> memCached;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">/**</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; * 添加一个指定的值到缓存中.<br />&nbsp;&nbsp;&nbsp;&nbsp; * </span><span style="color: #808080">@param</span><span style="color: #008000"> key<br />&nbsp;&nbsp;&nbsp;&nbsp; * </span><span style="color: #808080">@param</span><span style="color: #008000"> value<br />&nbsp;&nbsp;&nbsp;&nbsp; * </span><span style="color: #808080">@return</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">*/</span><span style="color: #000000"><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">boolean</span><span style="color: #000000"> add(String key, Object value)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> mcc.add(key, value);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">boolean</span><span style="color: #000000"> add(String key, Object value, Date expiry)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> mcc.add(key, value, expiry);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">boolean</span><span style="color: #000000"> replace(String key, Object value)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> mcc.replace(key, value);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">boolean</span><span style="color: #000000"> replace(String key, Object value, Date expiry)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> mcc.replace(key, value, expiry);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">/**</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; * 根据指定的关键字获取对象.<br />&nbsp;&nbsp;&nbsp;&nbsp; * </span><span style="color: #808080">@param</span><span style="color: #008000"> key<br />&nbsp;&nbsp;&nbsp;&nbsp; * </span><span style="color: #808080">@return</span><span style="color: #008000"><br />&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000">*/</span><span style="color: #000000"><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> Object get(String key)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> mcc.get(key);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemCached cache </span><span style="color: #000000">=</span><span style="color: #000000"> MemCached.getInstance();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff0000">cache.add(</font></span><font color="#ff0000"><span style="color: #000000">"</span><span style="color: #000000">hello</span><span style="color: #000000">"</span><span style="color: #000000">, </span><span style="color: #000000">234</span></font><span style="color: #000000"><font color="#ff0000">);<br /></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.print(</span><span style="color: #000000">"</span><span style="color: #000000">get value : </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> cache.get(</span><span style="color: #000000">"</span><span style="color: #000000">hello</span><span style="color: #000000">"</span><span style="color: #000000">));<br /><br />&nbsp;&nbsp;&nbsp; }<br />}<br /></span></p>
<p>&nbsp;&nbsp;&nbsp; 那么我们就可以通过简单的像main方法中操作的一样存入一个变量，然后再取出进行查看，我们可以看到先调用了add，然后再进行get，我们运行一次后，234这个值已经被我们存入了memcached的缓存中的了，我们将main方法中红色的那一行注释掉后，我们再运行还是可以看到get到的value也是234，即缓存中我们已经存在了数据了。</p>
<p>&nbsp;&nbsp;&nbsp; 对基本的数据我们可以操作，对于普通的POJO而言，如果要进行存储的话，那么比如让其实现java.io.Serializable接口，因为memcached是一个分布式的缓存服务器，多台服务器间进行数据共享需要将对象序列化的，所以必须实现该接口，否则会报错的。比如我们写一个简单的测试Bean如下：</p>
<p><span style="color: #0000ff">class</span><span style="color: #000000"> TBean </span><span style="color: #0000ff">implements</span><span style="color: #000000"> java.io.Serializable<br />{<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">final</span><span style="color: #000000"> </span><span style="color: #0000ff">long</span><span style="color: #000000"> serialVersionUID </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">1945562032261336919L</span><span style="color: #000000">;<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">private</span><span style="color: #000000"> String name;<br /><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> String getName()<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">return</span><span style="color: #000000"> name;<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> setName(String name)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff">this</span><span style="color: #000000">.name </span><span style="color: #000000">=</span><span style="color: #000000"> name;<br />&nbsp;&nbsp;&nbsp; }<br />}<br /></span></p>
<p>&nbsp;&nbsp;&nbsp; 然后我们在main方法中加入如下几行代码：</p>
<p>TBean tb <span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> TBean();<br />tb.setName(</span><span style="color: #000000">"</span><span style="color: #000000">铁木箱子</span><span style="color: #000000">"</span><span style="color: #000000">);<br />cache.add(</span><span style="color: #000000">"</span><span style="color: #000000">bean</span><span style="color: #000000">"</span><span style="color: #000000">, tb);<br />TBean tb1 </span><span style="color: #000000">=</span><span style="color: #000000"> (TBean)cache.get(</span><span style="color: #000000">"</span><span style="color: #000000">bean</span><span style="color: #000000">"</span><span style="color: #000000">);<br />System.out.print(</span><span style="color: #000000">"</span><span style="color: #000000">name=</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> tb1.getName());<br />tb1.setName(</span><span style="color: #000000">"</span><span style="color: #000000">铁木箱子_修改的</span><span style="color: #000000">"</span><span style="color: #000000">);<br />tb1 </span><span style="color: #000000">=</span><span style="color: #000000"> (TBean)cache.get(</span><span style="color: #000000">"</span><span style="color: #000000">bean</span><span style="color: #000000">"</span><span style="color: #000000">);<br />System.out.print(</span><span style="color: #000000">"</span><span style="color: #000000">name=</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> tb1.getName());</span></p>
<p><span style="color: #000000">&nbsp;&nbsp;&nbsp; 我们首先把TBean的一个实例放入缓存中，然后再取出来，并进行名称的修改，然后我们再取这个对象，我们再看其名称，发现修改的对象并不是缓存中的对象，而是通过序列化过来的一个实例对象，这样我们就无须担心对原生类的无意修改导致缓存数据失效了，呵呵~~看来我也是多此一想啊。所以这表明从缓存中获取的对象是存入对象的一个副本，对获取对象的修改并不能真正的修改缓存中的数据，而应该使用其提供的replace等方法来进行修改。</span></p>
<p><span style="color: #000000">&nbsp;&nbsp;&nbsp; </p>
<p>查看缓存区块，分析结果可调配-f参数<br />telnet 127.0.0.1 11212</p>
<p>查看缓存的各种状态</p>
<p>stats<br />====================================<br />查看缓存所分配的slabs</p>
<p>stats slabs</p>
<p>====================================<br />清除统计数据</p>
<p>stats reset====================================<br />是否显示详细操作记录(每一个键值get、set、hit、del的次数)<br />stats detail on，记录详细操作</p>
<p>stats detail dump，不记录详细操作<br />====================================<br />显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)<br />stats items<br />====================================<br />显示某个slab中的前limit_num个key列表，显示格式如下<br />stats cachedump slab_id 显示条数</p>
<p><br />参考资料<br />一般来说一个memcahced进程会预先将自己划分为若干个slab，每个slab下又有若干个page，每个page下又有多个chunk，如果我们把这3个咚咚看作是object得话，这是两个一对多得关系。再一般来说，slab得数量是有限得，几个，十几个，或者几十个，这个跟进程配置得内存有关。而每个slab下得page默认情况是1m，也就是说如果一个slab占用100m得内存得话，那么默认情况下这个slab所拥有得page得个数就是100，而chunk就是我们得数据存放得最终地方。chunk_size表示数据存放块得大小，chunks_per_page表示一个内存页page中拥有得chunk得数量，total_pages表示每个slab下page得个数。total_chunks表示这个slab下chunk得总数（＝total_pages * chunks_per_page），used_chunks表示该slab下已经使用得chunk得数量，free_chunks表示该slab下还可以使用得chunks数量。</p>
<p></span>&nbsp;</p><img src ="http://www.blogjava.net/hao446tian/aggbug/373723.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hao446tian/" target="_blank">昊天</a> 2012-04-10 16:57 <a href="http://www.blogjava.net/hao446tian/archive/2012/04/10/373723.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>