﻿<?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-Kevin坎坷的程序人生-文章分类-memcache</title><link>http://www.blogjava.net/sway/category/35221.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 15 Oct 2008 04:44:34 GMT</lastBuildDate><pubDate>Wed, 15 Oct 2008 04:44:34 GMT</pubDate><ttl>60</ttl><item><title>memcache分析调试</title><link>http://www.blogjava.net/sway/articles/234409.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 04:28:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234409.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234409.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234409.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234409.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234409.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1.关于本文档&nbsp; &nbsp; &nbsp; &nbsp;本文档所有的分析都是在1.2版本之上,偶尔会提到比较1.1版本.其他版本没有阅读.&nbsp; &nbsp; &nbsp; &nbsp;一个星期时间的工作,不可能对memcache有很深刻的分析.文档本身的目的在于为以后的研究准备一个总结资料.刚接触memcache时,对其设计分布式的思路感到十分欣喜,因为在中间层...&nbsp;&nbsp;<a href='http://www.blogjava.net/sway/articles/234409.html'>阅读全文</a><img src ="http://www.blogjava.net/sway/aggbug/234409.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 12:28 <a href="http://www.blogjava.net/sway/articles/234409.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcache通信协议(转自博客园)</title><link>http://www.blogjava.net/sway/articles/234406.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:55:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234406.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234406.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234406.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234406.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234406.html</trackback:ping><description><![CDATA[<p><a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607268"><span style="font-family: 宋体;">协议</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607269"><span style="font-family: 宋体;">关键字 Keys</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607270"><span style="font-family: 宋体;">命令Commands</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607271"><span style="font-family: 宋体;">超时时间&nbsp;Expiration times</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607272"><span style="font-family: 宋体;">错误信息 Error strings</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607273"><span style="font-family: 宋体;">存储命令 Storage commands</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607274"><span style="font-family: 宋体;">读取命令 Retrieval command</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607275"><span style="font-family: 宋体;">删除 Deletion</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607276"><span style="font-family: 宋体;">增加/<span style="font-family: 宋体;">减少 Increment/Decrement</span></span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607277"><span style="font-family: 宋体;">统计 Statistics</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607278"><span style="font-family: 宋体;">多用途统计&nbsp;General-purpose statistics</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607279"><span style="font-family: 宋体;">其他命令 Other commands</span></a><br />
<a href="http://www.cnblogs.com/kevintian/articles/1197681.html#_Toc198607280">UDP<span style="font-family: 宋体;">协议&nbsp;UDP protocol</span></a></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">协议</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcached<span style="font-family: 宋体;">的客户端通过</span>TCP<span style="font-family: 宋体;">连接与服务器通信（</span>UDP<span style="font-family: 宋体;">协议的接口也可以使用，详细说明请参考</span>&#8221;UDP <span style="font-family: 宋体;">协议</span>&#8221;<span style="font-family: 宋体;">部分）。一个给定的运行中的</span>memcached<span style="font-family: 宋体;">服务器在某个（可配置的）端口上监听连接；客户端连接该端口，发送命令给服务器，读取反馈，最后关闭连接。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">没有必要发送一个专门的命令去结束会话。客户端可以在不需要该连接的时候就关闭它。注意：我们鼓励客户端缓存它们与服务器的连接，而不是每次要存储或读取数据的时候再次重新建立与服务器的连接。</span>memcache<span style="font-family: 宋体;">同时打开很多连接不会对性能造成到大的影响，这是因为</span>memcache<span style="font-family: 宋体;">在设计之处，就被设计成即使打开了很多连接（数百或者需要时上千个连接）也可以高效的运行。缓存连接可以节省与服务器建立</span>TCP<span style="font-family: 宋体;">连接的时间开销（于此相比，在服务器段为建立一个新的连接所做准备的开销可以忽略不计）。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcache<span style="font-family: 宋体;">通信协议有两种类型的数据：文本行和非结构化数
据。文本行用来发送从客户端到服务器的命令以及从服务器回送的反馈信息。非结构化的数据用在客户端希望存储或者读取数据时。服务器会以字符流的形式严格准
确的返回相应数据在存储时存储的数据。服务器不关注字节序，它也不知道字节序的存在。</span>memcahce<span style="font-family: 宋体;">对非结构化数据中的字符没有任何限制，可以是任意的字符，读取数据时，客户端可以在前次返回的文本行中确切的知道接下来的数据块的长度。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">文本行通常以&#8220;</span>"r"n<span style="font-family: 宋体;">&#8221;结束。非结构化数据通常也是以&#8220;</span>"r"n<span style="font-family: 宋体;">&#8221;结束，尽管</span>"r<span style="font-family: 宋体;">、</span>"n<span style="font-family: 宋体;">或者其他任何</span>8<span style="font-family: 宋体;">位字符可以出现在数据块中。所以当客户端从服务器读取数据时，必须使用前面提供的数据块的长度，来确定数据流的结束，二不是依据跟随在字符流尾部的&#8220;</span>"r"n<span style="font-family: 宋体;">&#8221;来确定数据流的结束，尽管实际上数据流格式如此。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">关键字</span><span style="font-size: 10.5pt; line-height: 240%;"> Keys</span></h1>
<p>memcached<span style="font-family: 宋体;">使用关键字来区分存储不同的数据。关键字是一个字符串，可以唯一标识一条数据。当前关键字的长度限制是</span>250<span style="font-family: 宋体;">个字符（当然目前客户端似乎没有需求用这么长的关键字）；关键字一定<span style="color: red;">不能</span>包含<span style="color: red;">控制字符</span>和<span style="color: red;">空格</span>。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">命令</span><span style="font-size: 10.5pt; line-height: 240%;">Commands</span></h1>
<p>memcahe<span style="font-family: 宋体;">有</span>3<span style="font-family: 宋体;">种类型的命令：</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">存储命令</span>—<span style="font-family: 宋体;">（</span>3<span style="font-family: 宋体;">个命令：</span>set<span style="font-family: 宋体;">、</span>add<span style="font-family: 宋体;">和</span>replace<span style="font-family: 宋体;">）要求服务器安装关键字存储数据。客户端发送一个命令行，然后一个数据块；命令执行后客户端等待一行反馈，用来表示命令执行成功与否。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">读取命令</span>-- <span style="font-family: 宋体;">（只有</span>1<span style="font-family: 宋体;">个命令：</span>get<span style="font-family: 宋体;">）
要求服务器根据一组关键字读取数据（在一个请求总可以包含一个或多个关键字）。客户端发送一个包含请求关键字的命令行；命令传递到服务器后，服务器就查找
每一个关键字下的数据，然户将数据以&#8220;一个关键字数据，一个反馈信息行跟着一个数据块&#8221;的格式回送数据，直到服务器发送&#8220;</span>END<span style="font-family: 宋体;">&#8221;的反馈行。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">其他命令，如</span>flush_all<span style="font-family: 宋体;">，</span>version<span style="font-family: 宋体;">等。这些命令不使用非结构化的数据。对于这些命令，客户端发送一个文本的命令行，根据命令的特性等待一行数据或者在最后一行以&#8220;</span>END<span style="font-family: 宋体;">&#8220;结尾的几行反馈信息。</span></p>
<p style="text-indent: 21pt;"><span style="font-family: 宋体;">所有的命令行总是以命令的名字开始，紧接着是以空格分割的参数。命令名称都是小写，并且是大小写敏感的。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">超时时间</span><span style="font-size: 10.5pt; line-height: 240%;">&nbsp;Expiration times</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">一些发送到服务器的命令包含超时时间（该超时时间对应于：数据项保存时间；客户端操作限时）。在这些例子中，被发送的真实时间要么是</span>UNIX<span style="font-family: 宋体;">时间戳（自</span>1970<span style="font-family: 宋体;">年</span>1<span style="font-family: 宋体;">月</span>1<span style="font-family: 宋体;">日</span><span style="font-family: 宋体;">零时起的秒数数值），或者从当前时间开始算起的秒数。对于后一种情况，秒数的数值不能超过</span>60*60*24*30<span style="font-family: 宋体;">（</span>30<span style="font-family: 宋体;">天的秒数）；如果秒数的数值大于了这个数值，服务器会认为该数值是</span>UNIX<span style="font-family: 宋体;">时间戳，而不是自当前时间开始的秒数偏移值。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">错误信息</span><span style="font-size: 10.5pt; line-height: 240%;"> Error strings</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">每个命令都有可能被反馈以一个错误消息。这些错误消息有以下三个类型：</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>&#8220;ERROR"r"n&#8221;&nbsp;</p>
<p style="margin-left: 21pt; text-indent: 21pt;"><span style="font-family: 宋体;">意味着客户端发送了一个在协议中不存在的命令。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"CLIENT_ERROR &lt;error&gt;"r"n"</p>
<p style="margin-left: 42pt;"><span style="font-family: 宋体;">表示客户端输入的命令行上存在某种错误，输入不符合协议规定。</span>&lt;error&gt;<span style="font-family: 宋体;">是一个人工可读（</span>human-readable<span style="font-family: 宋体;">）的错误注释。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"SERVER_ERROR &lt;error&gt;"r"n"</p>
<p style="margin-left: 42pt;"><span style="font-family: 宋体;">表示服务器在执行命令时发生了某些错误，致使服务器无法执行下去。</span>&lt;error&gt;<span style="font-family: 宋体;">也是一个人工可读（</span>human-readable<span style="font-family: 宋体;">）的错误注释。在一些情况下，错误导致服务器不能再为客户端服务（这样的情况很少发生），服务器就会在发生错误消息后主动关闭连接。<span style="color: red;">这也是服务器主动关闭到客户端连接的唯一情况。</span></span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">后续秒数各种命令的时候，我们不再赘述错误消息的情况，当我们要清楚错误是存在的，不可忽略。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">存储命令</span><span style="font-size: 10.5pt; line-height: 240%;"> Storage commands</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">首先，客户端发生如下这样的命令：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>&lt;command name&gt; &lt;key&gt; &lt;flags&gt; &lt;exptime&gt; &lt;bytes&gt;"r"n</p>
            <p>&lt;data block&gt;"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="text-indent: 21pt;"><span style="font-family: 宋体;">其中：</span></p>
<p style="text-indent: 21pt;">&lt;command name&gt; <span style="font-family: 宋体;">是</span> set<span style="font-family: 宋体;">、</span>add<span style="font-family: 宋体;">或者</span>replace<span style="font-family: 宋体;">。</span>set<span style="font-family: 宋体;">表示存储该数据；</span>add<span style="font-family: 宋体;">表示如果服务器没有保存该关键字的情况下，存储该数据；</span>replace<span style="font-family: 宋体;">表示在服务器已经拥有该关键字的情况下，替换原有内容。</span></p>
<p style="text-indent: 21pt;">&lt;key&gt;<span style="font-family: 宋体;">是客户端要求服务器存储数据的关键字。</span></p>
<p style="text-indent: 21pt;">&lt;flags&gt;<span style="font-family: 宋体;">是一个</span>16<span style="font-family: 宋体;">位的无符号整数，服务器将它和数据一起存储并且当该数据被检索时一起返回。客户端可能使用该数值作为一个位图来存储特殊数据信息；这个字段对服务器不是透明的。</span></p>
<p style="text-indent: 21pt;">&lt;exptime&gt;<span style="font-family: 宋体;">是超时时间。如果值为</span>0<span style="font-family: 宋体;">表示该数据项永远不超时（但有时候该数据项可能被删除以为其他数据腾出空间）；如果值不为</span>0<span style="font-family: 宋体;">，可能是绝对的</span>UNIX<span style="font-family: 宋体;">时间，也可能是自现在开始的偏移值，它保证客户段在这个超时时间到达后，客户端将取不到该数据项。</span></p>
<p style="text-indent: 21pt;">&lt;bytes&gt;<span style="font-family: 宋体;">是随后数据的字节数，不包括终结符</span>&#8221;"r"n&#8221;<span style="font-family: 宋体;">。</span>&lt;bytes&gt;<span style="font-family: 宋体;">有可能是</span>0<span style="font-family: 宋体;">，它后面将是一个空的数据块。</span></p>
<p style="text-indent: 21pt;">&lt;data block&gt;<span style="font-family: 宋体;">是真正要存储数据流。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">发送命令行和数据后，客户端等待反馈，可以是如下几种情况：</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"STORED"r"n"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">表示存储数据成功。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"NOT_STORED"r"n" &nbsp;&nbsp; <span style="font-family: 宋体;">表示发送的数据没有存储，但这不因为错误，而是发生在</span>add<span style="font-family: 宋体;">或者</span>replace<span style="font-family: 宋体;">命令不能满足条件时，或者数据项正处于要删除的队列中。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">错误消息</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">读取命令</span><span style="font-size: 10.5pt; line-height: 240%;"> Retrieval command:</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">读取命令如下所示：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>get &lt;key&gt;*"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&lt;key&gt;*<span style="font-family: 宋体;">表示一个或多个使用空格分割的关键字字符串。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">发送命令后，客户端等待返回一个或多个数据项，每个数据项的格式是一个文本行，后跟着一个数据块。当所有的数据项发送完毕后，服务器发送字符串</span>&#8221;END"r"n&#8221;<span style="font-family: 宋体;">表示服务器反馈数据的结束。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">返回数据项的格式如下：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>VALUE &lt;key&gt; &lt;flags&gt; &lt;bytes&gt;"r"n</p>
            <p>&lt;data block&gt;"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="text-indent: 21pt;">&lt;key&gt;<span style="font-family: 宋体;">是发生数据项的关键字。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;flags&gt;<span style="font-family: 宋体;">是存储该数据项时，客户端命令中的标志字段。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bytes&gt;<span style="font-family: 宋体;">是紧跟文本行后数据块的长度，不包括终结符</span>&#8221;"r"n&#8221;<span style="font-family: 宋体;">。</span></p>
<p style="text-indent: 21pt;">&lt;data block&gt;<span style="font-family: 宋体;">是数据项的数据部分。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">如果请求命令行中的有些关键字对应的数据项没有被返回，这意味着服务器没有该关键字标示下的数据项（有可能是从来没有被存储过，或者存储过但被删除掉以腾出内存空间，或者数据项超时了，再或者它被某个客户端删除了）。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">删除</span><span style="font-size: 10.5pt; line-height: 240%;"> Deletion</span></h1>
<p style="text-indent: 21pt;"><span style="font-family: 宋体;">删除命令允许直接删除数据项，命令格式如下：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>delete &lt;key&gt; &lt;time&gt;"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="text-indent: 21pt;">&lt;key&gt;<span style="font-family: 宋体;">是客户端希望服务器删除数据项的关键字</span></p>
<p style="text-indent: 21pt;">&lt;time&gt;<span style="font-family: 宋体;">是客户端希望服务器阻止</span>add<span style="font-family: 宋体;">和</span>replace<span style="font-family: 宋体;">命令使用该关键字数据项的秒数，可以是相对时间也可以是</span>UNIX<span style="font-family: 宋体;">的绝对时间。在这段时间内，数据项被放入一个删除队列，它不能被</span>get<span style="font-family: 宋体;">命令读取，在其上使用</span>add<span style="font-family: 宋体;">和</span>replace<span style="font-family: 宋体;">也会失败，但使用</span>set<span style="font-family: 宋体;">命令可以成功。当这个时间过去后，数据项从服务器的内存中真正的删除。该参数是可选参数，如果不存在默认为</span>0<span style="font-family: 宋体;">，这意味着立即从服务器上删除。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">服务器返回信息：</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"DELETED"r"n"&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">表示数据项删除成功</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"NOT_FOUND"r"n" &nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">表示该关键字指定的数据项在服务器上没有找到</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">其他错误消息</span></p>
<p style="margin-left: 21pt;"><span style="font-family: 宋体;">下面的</span>flush_all<span style="font-family: 宋体;">命令使得所有存在的数据项立即失效。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">增加</span><span style="font-size: 10.5pt; line-height: 240%;">/</span><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">减少</span><span style="font-size: 10.5pt; line-height: 240%;"> Increment/Decrement</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8220;incr&#8221;<span style="font-family: 宋体;">和</span>&#8221;decr&#8221;<span style="font-family: 宋体;">命令用来修改以及存在的数据项的内容，增加或者减少它。该数据被当作</span>32<span style="font-family: 宋体;">位无符号整数处理。如果当前数据非此类数据，则经将该内容当作</span>0<span style="font-family: 宋体;">来处理。另外在其上施加</span>incr/decr<span style="font-family: 宋体;">命令的数据项必须是业已存在的；对于不存在的数据项不会将它作为</span>0<span style="font-family: 宋体;">对待，而是以错误结束。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">客户端发送命令行如下格式：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>incr &lt;key&gt; &lt;value&gt;"r"n</p>
            <p><span style="font-family: 宋体;">或者</span></p>
            <p>decr &lt;key&gt; &lt;value&gt;"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p style="text-indent: 21pt;">&lt;key&gt;<span style="font-family: 宋体;">是客户端要修改数据项的关键字</span></p>
<p style="text-indent: 21pt;">&lt;value&gt;<span style="font-family: 宋体;">是对该数据行进行增加或者减少的操作数。它是一个</span>32<span style="font-family: 宋体;">位的无符号整数。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">反馈信息有如下几种：</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"NOT_FOUND"r"n"</p>
<p style="margin-left: 21pt; text-indent: 21pt;"><span style="font-family: 宋体;">表明在服务器上没有找到该数据项。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"&lt;value&gt;"r"n "</p>
<p style="margin-left: 21pt; text-indent: 21pt;">value<span style="font-family: 宋体;">是执行完增加</span>/<span style="font-family: 宋体;">减少命令后，该数据项新的数值。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">错误信息。</span></p>
<p style="text-indent: 21pt;"><span style="font-family: 宋体;">注意到&#8220;</span>decr<span style="font-family: 宋体;">&#8221;命令的下溢问题，如果客户端尝试减少的数量小于</span>0<span style="font-family: 宋体;">，其结果是</span>0<span style="font-family: 宋体;">。&#8220;</span>incr<span style="font-family: 宋体;">&#8221;命令的溢出问题没有检查。另外减少一个数据而使它减少了长度，但不保证减少它返回时的长度。该数字可能是附加空格的数字，但这只是实现的优化，所以你不能相信它。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">统计</span><span style="font-size: 10.5pt; line-height: 240%;"> Statistics</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">&#8220;</span>stats<span style="font-family: 宋体;">&#8221;命令用来查询服务器的运行情况和其他内部数据。它有两种情况，以有无参数来区分：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>stats"r"n</p>
            <p><span style="font-family: 宋体;">或者</span></p>
            <p>stats &lt;args&gt;"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">第一种情况它导致服务器输出一般统计信息以及设置信息和文档化内容。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">第二种情况根据</span>&lt;args&gt;<span style="font-family: 宋体;">具体的参数，服务器发送各种内部数据。这部分没有在协议中文档化，因为与</span>memcache<span style="font-family: 宋体;">的开发者有关其可能是随时变化的。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">多用途统计</span><span style="font-size: 10.5pt; line-height: 240%;">&nbsp;General-purpose statistics</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">当接收到没有带参数的&#8220;</span>stats<span style="font-family: 宋体;">&#8221;命令后，服务器发送许多类似与如下格式的文本行：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>STAT &lt;name&gt; &lt;value&gt;"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">当类似的文本行全部发送完毕后，服务器发送如下的文本行结束反馈信息：</span></p>
<p>
<table style="background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>END"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">在所有</span>STAT<span style="font-family: 宋体;">文本行中，</span>&lt;name&gt;<span style="font-family: 宋体;">是该统计项目的名称，</span>&lt;value&gt;<span style="font-family: 宋体;">是其数据。下面是一份</span>stats<span style="font-family: 宋体;">命令反馈的所有统计项目的列表，后面跟着其值的数据类型。在数据类型列中，</span>&#8221;32u&#8221;<span style="font-family: 宋体;">表示一个</span>32<span style="font-family: 宋体;">位无符号整数，</span>&#8221;64u&#8221;<span style="font-family: 宋体;">表示一个</span>64<span style="font-family: 宋体;">位无符号整数，</span>&#8221;32u:32u&#8221;<span style="font-family: 宋体;">表示是两个用冒号分割的</span>32<span style="font-family: 宋体;">位无符号整数。</span></p>
<p>
<table style="border: medium none ; border-collapse: collapse;" border="1" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 86.4pt;" valign="top" width="115">
            <p style="text-align: center;" align="center"><strong><span style="font-family: 宋体;">名称</span></strong></p>
            </td>
            <td style="padding: 0cm 5.4pt; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 54pt;" valign="top" width="72">
            <p style="text-align: center;" align="center"><strong><span style="font-family: 宋体;">值类型</span></strong></p>
            </td>
            <td style="padding: 0cm 5.4pt; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 285.7pt;" valign="top" width="381">
            <p style="text-align: center;" align="center"><strong><span style="font-family: 宋体;">含义</span></strong></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>pid</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器进程的进程号</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>uptime</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器自运行以来的秒数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>time</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">当前服务器上的</span>UNIX<span style="font-family: 宋体;">时间</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>version</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>string</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器的版本字符串</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>rusage_user</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u:32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器进程积累的用户时间（秒</span>:<span style="font-family: 宋体;">微妙）</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>rusage_system</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u:32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器进程积累的系统时间（秒</span>:<span style="font-family: 宋体;">微妙）</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>curr_items</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">当前在服务器上存储的数据项的个数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>total_items</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">在服务器上曾经保存过的数据项的个数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>bytes</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">当前服务器上保存数据的字节数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>curr_connections</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">处于打开状态的连接数目</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>total_connections</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">曾经打开过的所有连接的数目</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>connection_structures</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>32u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器分配的连接结构体的个数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>cmd_get</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p>get<span style="font-family: 宋体;">命令请求的次数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>cmd_set</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">存储命令请求的次数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>get_hits</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">关键字获取命中的次数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>get_misses</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">关键字获取没有命中的次数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>evictions</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">所有因超时而被替换出内存的数据项的个数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>bytes_read</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器从网络上读取到的字节数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>bytes_write</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器向网络上写的字节数</span></p>
            </td>
        </tr>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 86.4pt;" valign="top" width="115">
            <p>limit_maxbytes</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 54pt;" valign="top" width="72">
            <p>64u</p>
            </td>
            <td style="padding: 0cm 5.4pt; width: 285.7pt;" valign="top" width="381">
            <p><span style="font-family: 宋体;">服务器允许存储数据的最大值</span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<h1><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">其他命令</span><span style="font-size: 10.5pt; line-height: 240%;"> Other commands</span></h1>
<p style="text-indent: 21pt;"><strong>"flush_all"</strong><span style="font-family: 宋体;">是一个带有可选数字参数的命令，它的执行总是成功的，服务器总是响应以</span>"OK"r"n"<span style="font-family: 宋体;">字符串。它的作用是使得所有的数据项立即（默认）或者经过一个指定的超时时间后全部失效。在置数据项失效后，对于读取命令将不会返回任何内容，除非在失效后这些数据再次被存储。</span>flush_all<span style="font-family: 宋体;">并没有真正的释放这些存在过的数据项占用的内存空间；数据空间真实被占用的情况发生在使用新的数据项覆盖老的数据项时。该命令作用最准确的定义是：它导致所有更新时间早于该命令设定的时间点的数据项，在被检索时被忽略，其表现就像已被删除了一样。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">使用带有延时</span>flush_all<span style="font-family: 宋体;">命令的目的是，当你有个</span>memcached<span style="font-family: 宋体;">服务器池，需要刷新所有的内容时，但不能在同一时间刷洗所有的服务器，这样就可能因为所有的服务器突然都要重新建立数据内容，而导致数据库压力的颠簸。延时选项允许你设置他们隔</span>10<span style="font-family: 宋体;">秒失效（设置第一个延时为</span>0<span style="font-family: 宋体;">，第二个</span>10<span style="font-family: 宋体;">秒，第三个</span>20<span style="font-family: 宋体;">秒等等）。</span></p>
<p style="text-indent: 21pt;"><strong>"version"</strong><span style="font-family: 宋体;">是一个没有参数的命令，命令格式如下：</span></p>
<p>
<table style="border: medium none ; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>version"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">服务器发回的反馈信息如下：</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>"VERSION &lt;version&gt;"r"n"</p>
<p style="text-indent: 21pt;">&lt;version&gt;<span style="font-family: 宋体;">是从服务器返回的版本字符串。</span></p>
<p style="margin-left: 42pt; text-indent: -21pt;"><span style="font-family: Wingdings;">l<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: 宋体;">错误消息。</span></p>
<p style="text-indent: 21pt;">"<strong>verbosity</strong>"<span style="font-family: 宋体;">是一个带有数字参数的命令。它的执行总是成功的，服务器反馈以</span>"OK"r"n"<span style="font-family: 宋体;">表示执行完成。它用来设置日志输出的详细等级。</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p style="text-indent: 21pt;">"<strong>quit</strong>"<span style="font-family: 宋体;">是一个没有参数的命令。其格式如下：</span></p>
<p>
<table style="border: medium none ; background: #e6e6e6 none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; border-collapse: collapse;" border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td style="padding: 0cm 5.4pt; width: 426.1pt;" valign="top" width="568">
            <p>quit"r"n</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">当服务器接受到此命令后，就关闭与该客户的连接。不管怎样，客户端可以在任意不需要该连接的时刻关闭它，而不需要发送该命令。</span></p>
<h1><span style="font-size: 10.5pt; line-height: 240%;">UDP</span><span style="font-size: 10.5pt; line-height: 240%; font-family: 宋体;">协议</span><span style="font-size: 10.5pt; line-height: 240%;">&nbsp;UDP protocol</span></h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">当基于</span>TCP<span style="font-family: 宋体;">协议的连接数超过</span>TCP<span style="font-family: 宋体;">连接的上限时，我们可以使用</span>UDP<span style="font-family: 宋体;">协议来替代。但是</span>UDP<span style="font-family: 宋体;">协议接口不提供可靠的传输，所以多用在不严格要求成功的操作上；典型的</span>get<span style="font-family: 宋体;">请求会因为缓存的问题，引起丢失或者不完整的传输。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">每个</span>UDP<span style="font-family: 宋体;">数据包包含一个简单的帧头，接着就是如</span>TCP<span style="font-family: 宋体;">协议描述的数据格式的数据流。在当前的实现中，请求必须包含在一个单独的</span>UDP<span style="font-family: 宋体;">数据包中，但返回可能分散在多个数据包中。（唯一的可以拆分请求数据包的是大的多关键字</span>get<span style="font-family: 宋体;">请求和</span>set<span style="font-family: 宋体;">请求，鉴于可靠性相比而言他们更适合用</span>TCP<span style="font-family: 宋体;">传输。）</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">帧头有</span>8<span style="font-family: 宋体;">字节长，如下是其格式（所有的数字都是</span>16<span style="font-family: 宋体;">位网络字节序整形，高位在前）：</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 - 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">请求</span>ID</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2 - 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">序列号</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4 - 5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">在当前的消息中含有的数据包的个数</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6-7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">保留以后使用，当前必须为</span>0</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">请求</span>ID<span style="font-family: 宋体;">由客户端提供。它的典型值是一个从随机种子开始递增值，实际上客户端可以使用任意的请求</span>ID<span style="font-family: 宋体;">。服务器的反馈信息中包含了和请求命令中一样的请求</span>ID<span style="font-family: 宋体;">。客户端凭借这个请求</span>ID<span style="font-family: 宋体;">区分来自于同一服务器的反馈。每一个包含未知请求</span>ID<span style="font-family: 宋体;">的数据包，可能是由于延时反馈造成，这些数据包都应该抛弃不用。</span></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-family: 宋体;">序列号从</span>0<span style="font-family: 宋体;">到</span>n-1<span style="font-family: 宋体;">，</span>n<span style="font-family: 宋体;">是消息中总的数据包的个数。客户端按照序列号排序重组数据包；结果序列中包含了一个完整的如</span>TCP<span style="font-family: 宋体;">协议一样格式的反馈信息（包含了&#8220;</span>"r"n<span style="font-family: 宋体;">&#8221;总结字符串）。</span></p>
<img src ="http://www.blogjava.net/sway/aggbug/234406.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:55 <a href="http://www.blogjava.net/sway/articles/234406.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cache Insight (转自javaeye)</title><link>http://www.blogjava.net/sway/articles/234404.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:49:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234404.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234404.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234404.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234404.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234404.html</trackback:ping><description><![CDATA[<br />
Cache Insight
<br />
<br />
前言
<br />
首先，介绍一下我（作者）自己使用Cache的背景，以便读者更清楚地了解我下面要讲述哪些内容。
<br />
我主要是一个Cache实现者，而不是使用者。为了给一些ORM（比如JPA实现）提供Cache支持，我需要包装其它的Open Source Cache，并考察它们的特性。
<br />
我对这些Open Source Cache的一些工作原理，了解得比较多。具体配置和使用细节，了解的比较少。
<br />
本文主要讲述的也是Cache的特性和工作原理，而不是一个安装、配置、使用的入门手册。
<br />
本文简述Cache的一般特性，详述Cache的高级特性，比如，分布式Cache，关联对象的Cache，POJO Cache等。
<br />
阅读本文需要具备基本的Cluster知识，ORM知识，数据库事务知识。本文不解释这些基本概念。
<br />
<br />
-------------------------------------------------------
<br />
Cache Features
<br />
首先，我们来浏览一下常见的Cache。
<br />
这个链接给出了常用的Java Open Source Cache。
<br />
http://java-source.net/open-source/cache-solutions
<br />
<br />
memcached，JBoss Cache，SwarmCache，OSCache，JCS，EHCache等开源项目的出镜率和关注率比较高。
<br />
memcached和其他几个不同，后面会详述。
<br />
JBoss Cache的特点是，功能大而全，可算是Cache集大成者，几乎什么都支持。
<br />
其余的几个都很轻量。SwarmCache，OSCache，JCS支持Cluster。EHCache不支持Cluster。
<br />
<br />
下面列出Cache的基本特性。
<br />
1. 时间记录
<br />
数据进入Cache的时间。
<br />
<br />
2. timeout过期时间
<br />
Cache里面的数据多久过期
<br />
<br />
3. Eviction Policy 清除策略
<br />
Cache满了之后，根据什么策略，应该清除哪些数据。
<br />
比如，最不经常被访问的数据，最久没有访问到的数据。
<br />
<br />
4. 命中率
<br />
Cache的数据被选中的比率
<br />
<br />
5. 分级Cache
<br />
有些Cache有分级的概念。比如，几乎所有的Cache都支持Region分区的概念。可以指定某一类的数据存放在特定的Region里面。JBoss Cache可以支持更多的级别。
<br />
<br />
6. 分布式Cache
<br />
分布在不同计算机上的Cache
<br />
<br />
7. 锁，事务，数据同步
<br />
一些Cache提供了完善的锁，事务支持。
<br />
<br />
以上特性，大部分Cache都有相应的API支持。这些API很直观，也很简单，本文不打算展开讲述。
<br />
本文下面主要介绍，memcached和JBoss Cache这两个具有代表意义的Cache的高级特性，包括分布式Cache的支持。
<br />
<br />
-------------------------------------------------------
<br />
<br />
memcached
<br />
http://www.danga.com/memcached/
<br />
<br />
memcached是一个Client Server结构的远程Cache实现。
<br />
Server是用C写的，提供了多种语言的客户端API，包括Java, C#, Ruby, Python, PHP, Perl, C等多种语言。
<br />
memcached主要使用在Shared Nothing Architecture中。应用程序通过客户端API，从memcached server存取数据。
<br />
典型的应用，比如，用memcached作为数据库缓存。
<br />
也常有这样的用法，用memcached存放HTTP Session的数据。具体做法是包装Session Interface，截获setAttribute(), getAttribute()方法。
<br />
<br />
MemcachedSessionWrapper {
<br />
&nbsp; Object getAttribute( key ){
<br />
&nbsp;&nbsp;&nbsp; return memcachedClient.get (session.getId() + key);
<br />
&nbsp; }
<br />
&nbsp; void setAttribute( key, value ){
<br />
&nbsp;&nbsp;&nbsp; memcachedClient.setObject(session.getId() + key, value);
<br />
&nbsp; }
<br />
}
<br />
<br />
不同计算机上的应用程序通过一个IP地址来访问memcahced Server。
<br />
同一个key对应的数据，只存在于一台memcached server的一份内存中。
<br />
memcached server也可以部署在多台计算机上。Memcached通过key的hashcode来判断从哪台memcached
server上存取数据数据。我们可以看到，同一个key对应的数据，还是只存在于一台memcached server的一份内存中。
<br />
所以，memcached不存在数据同步的问题。这个特性很关键，我们后面讲到Cluster Cache的时候，就会涉及到数据同步的问题。
<br />
memcached由于是远程Cache，要求放到Cache的Key和Value都是Serializable。
<br />
远程Cache，最令人担心的网络通信开销。据有经验的人说，memcached网络通信开销很小。
<br />
memcached的API设计也是远程通信友好的，提供了getMulti()等高粒度的调用方法，能够批量获取数据，从而减少网络通信次数。
<br />
<br />
-------------------------------------------------------
<br />
<br />
JBoss Cache
<br />
http://www.jboss.org/products/jbosscache
<br />
<br />
有一个商业Cluster Cache，叫做tangosol。
<br />
JBoss Cache是我唯一知道的能够和tangosol媲美的开源Cache。
<br />
<br />
Cluster Cache的数据同步，需要网络通信，这就要求放到Cache的数据是Serializable。
<br />
JBoss Cache提出了POJO Cache的概念，意思是数据不是Serializable，一样能够在Cluster中同步。
<br />
JBoss POJO Cache通过AOP机制，支持对象同步，支持对象属性的同步，支持关联对象的Cache，支持继承，集合，Query，并支持不同级别的事务，俨然一个小型内存数据库级别的数据存储系统。
<br />
下面进行解释。
<br />
最令人迷惑不解的是这个POJO的Cluster同步如何实现。
<br />
JBoss POJO
Cache采用AOP来照管了POJO的通信和传播工作。天下没有免费的午餐，POJO不支持序列化，框架本身就要做这个工作——Marshal
and Unmarshal，比如通过把Java对象翻译成XML，传播出去，对方收到XML，再翻译成Java对象。
<br />
上面说了，JBoss POJO Cache很像一个小型存储容器，JBoss POJO Cache的对象管理也非常类似Hibernate，JDO，JPA等ORM工具，同样有Detach和Attach的概念。
<br />
Attach就是put，把对象放入到Cache中。Detach就是remove，把对象从Cache中删除。为啥要多起个名？
<br />
原因是，put的时候，放进去的是个干干净净的POJO，出来的时候，就是Enhanced Object，里面夹杂了很多Interceptor代码，监听对象的方法。
<br />
你操作这个对象的时候，JBoss AOP框架就获得了相应的通知，能够做出相应的反应，比如数据同步等。
<br />
JBoss POJO Cache支持对集合类型的AOP。同样需要把集合Attach（Put）进Cache，然后get出来，然后对集合进行操作，就可以被JBoss AOP截获了。
<br />
<br />
JBoss POJO Cache的基础是JBoss Tree Cache。这个Tree Cache类似于一个XML DOM树形数据结构。
<br />
JBoss Cache采用Full Qualified Name作为Cache Key，类似于XPath。比如，a/b/c/d。
<br />
当你删除a/b的时候，a/b/c，a/b/c/d等所有属于a/b的Key和对应数据，都被删除。
<br />
JBoss Cache的findObjects方法能够找出一串对象。比如，findObjects根据a/b/c/d能够找出a,b,c,d等4个对象，放在一个Map中返回。
<br />
具体用法要参见API详细说明，因为JBoss POJO Cache提供了很多行为模式。
<br />
这种分级的Cache功能很有用，实现起来也不难。只是，我觉得，还是不够强大。既然支持了类似于XPath的Key，不如索性支持XPath的
条件查询。比如，a[name=&#8221;n&#8221;]/b/c。当然，实现这种功能的代价非常大，需要遍历整个Cache
Tree，正如XPath需要遍历整个DOM节点一样。
<br />
<br />
最后，JBoss Cache和tangosol一样，都支持了一个我认为如同鸡肋一般的功能，锁机制和事务支持。这个事务支持的意思是，Cache本身实现了类似于数据库的4种事务隔离级别。
<br />
在我看来，这种支持无疑是为了赚取眼球。Cache不当做Cache来用，搞些歪门邪道，大而不当。想当作数据库来用，那还不如把主要功夫花在上述提到的那种精确批量查询功能上。
<br />
<br />
-------------------------------------------------------
<br />
Cluster同步
<br />
<br />
Cluster之间的Cache同步有多种实现方法。比如，JMS，RMI，Client Server
Socket等方法，用的最多的，支持最广的方法是JGroups开源项目实现的Multicast。配置Cluster
Cache，通常就相当于配置JGroups，需要阅读JGroups配置文档。
<br />
Cache的操作通常有4个，get，put，remove，clear。
<br />
对于Cluster
Cache来说，读操作（get）肯定是Local方法，只需要从本台计算机内存中获取数据。Remove/clear两个写操作，肯定是Remote方
法，需要和Cluster其他计算机进行同步。Put这个写方法，可以是Local，也可以是Remote的。
<br />
Remote Put方法的场景是这样，一台计算机把数据放到Cache里面，这个数据就会被传播到Cluster其他计算机上。这个做法的好处是Cluster各台计算机的Cache数据可以及时得到补充，坏处是传播的数据量比较大，这个代价比较大
<br />
Local
Put方法的场景是这样，一台计算机把数据放到Cache里面，这个数据不会被传播到Cluster其他计算机上。这个做法的好处是不需要传播数据，坏处
是Cluster各台计算机的Cache数据不能及时得到补充，这个不是很明显的问题，从Cache中得不到数据，从数据库获取数据是很正常的现象。
<br />
Local Put比起Remote Put的优势很明显，所以，通常的Cluster Cache都采用Local Put的策略。各Cache一般都提供了Local Put的配置选项，如果你没有看到这个支持，那么请换一个Cache。:D
<br />
<br />
-------------------------------------------------------
<br />
<br />
Center vs Cluster
<br />
Memcached可以看作是Center Cache。
<br />
Center Cache和Cluster Cache的特性比较如下：
<br />
Center Cache没有同步问题，所以，remove/clear的时候，比较有优势，不需要把通知发送到好几个计算机上。
<br />
但是，Center Cache的所有操作，get/put/remove/clear都是Remote操作。而Cluster Cache的get/put都是Local操作，所以，Cluster Cache在get/put操作上具有优势。
<br />
<br />
Local get/put在关联对象的组装和分拆方面，优势比较明显。
<br />
关联对象的分拆是这个意思。
<br />
比如，有一个Topic对象，下面有几个Post对象，每个Post对象都有一个User对象。
<br />
Topic对象存放到Cache中的时候，下面的关联对象都要拆开来，分成各自的Entity Region来存放。
<br />
Topic Region -&gt; Topic ID -&gt; Topic Object
<br />
Post Region -&gt; Post ID -&gt; Post Object
<br />
User Region -&gt; User ID -&gt; User Object
<br />
这个时候，put的动作可能发生多次。Remote Put的开销就比较大。
<br />
Get的过程类似，也需要get多次，才能拼装成一个完整的Topic对象。
<br />
<br />
<br />
-------------------------------------------------------
<br />
<br />
过期数据
<br />
Cache可以用在任何地方，比如，页面缓存。但Cache的最常用场景是用在ORM中，比如，Hibernate，JDO，JPA中。
<br />
ORM Cache的使用方法有个原则——不要把没有Commit的修改数据放入到缓存中。这是为了防止Read Dirty。
<br />
数据库事务分为两种，一种是读事务，不修改数据，一种是写事务，修改数据。
<br />
写事务的操作流程如下：
<br />
db.commt();
<br />
cache.remove(key); // 这一步操作，清除了Cache数据，也记录了一个时间removeTime。
<br />
<br />
读事务的操作流程如下：
<br />
readTime = current time;
<br />
data = cache.get(key);
<br />
if(data is null){
<br />
&nbsp; data = db.load(key);
<br />
&nbsp; cache.put(key, data, readTime); // 这里要readTime传进去
<br />
}
<br />
<br />
这里需要注意的是put的时候，需要readTime这个参数。
<br />
这个readTime要和上一次的removeTime进行比较。
<br />
如果readTime &gt; removeTime，这个put才能成功，数据才能够进入缓存。
<br />
这是为了保证不把过期数据放入到Cache中，及时反映数据库的变化。
<br />
<br />
另外，需要注意的是，cache.remove(key); 这个事件需要传播到Cluster其他计算机，通知它们清理缓存。
<br />
为什么需要这个通知？
<br />
一定要注意，这不是为了避免并发修改冲突。并发修改冲突的避免需要引入乐观锁版本控制机制。
<br />
有可能存在这样的误解，认为有了乐观锁版本控制机制，就不需要Cache.remove通知了。这是不对的。
<br />
Cache.remove通知的主要目的是，保证缓存能够及时清理过期数据，反映数据的变化，保证大部分时间内，应用程序显示给用户的不是过期数据。
<br />
另外，db.commt(); cache.remove(key); 这两步调用之间，有很小的可能发生另外的事务。这段极小的时间内，可能无法保证Read Committed，可能出现很短期的过期数据。
<br />
为什么说很短期，因为紧接着的Cache.remove就会清理过期数据。
<br />
如果偏执到这种程度，这么短期的几乎不可能发生的小概率事件，都不能容忍，那么可以，db.commt()之前，给Cache加一个悲观锁，不让别的事务，把数据Put进入Cache，就可以防止这个小概率、微影响的事件。
<br />
JBoss Cache和Tangosol就提供了这类鸡肋一般的悲观锁机制。典型的开发资源配置不当，有用的需要的不做，没用的功能使劲做。
<br />
ORM Query Cache
<br />
ORM Cache一般分为两种。一种是ID Cache（ORM文档中称为二级Cache），用来存放Entity ID对应的Entity对象；一种是Query Cache，用来存放一条查询语句对应的查询结果集。
<br />
ID Cache非常直观，如同上述讲述的，一般是一个Entity Class对应一个Region，Entity存放到对应的Region里面。
<br />
Query Cache比较复杂，而且潜在作用很大，值得仔细讲解。
<br />
现有的ORM对Query Cache的支持并不是很理想。
<br />
比如，Hibernate把整个结果集直接放在Query Cache中。这样，有任何风吹草动，发生了任何数据库的写操作，Query Cache都需要清空。
<br />
有一种比较好的做法，把ID List存放在Query Cache中，每次获取的时候，先获取ID List，然后根据ID
List获取Entity List。Query Cache根据Query涉及到的Table Name来进行清理，一旦发生对这些Table
Name的修改操作，就可以根据不同情况，清理Query Cache。
<br />
比如，select t2.* from t1, t2 where t1.id = t2.foreign_id and t1.name = &#8216;a&#8217;
<br />
那么insert into t1, delete from t1, insert into t2, delete from t2都会清除这条Query Cache。
<br />
同样的 update t1 set name = &#8230; 这样的语句也会清除这条Query Cache。
<br />
Hibernate为什么不这么做，因为Query Cache的情况比较复杂。也许选择的结果集并不是只有一个Entity类型，也许只是几个字段。
<br />
这个地方，如果细分，还是有很多功夫可以做的。而且也很值得花功夫做，因为Query Cache对于性能的提高，有很大作用。
<br />
<br />
-------------------------------------------------------
<br />
<br />
ORM Query Cache
<br />
Cache可以用在任何地方，比如，页面缓存。但Cache的最常用场景是用在ORM中，比如，Hibernate，JDO，JPA中。
<br />
ORM Cache一般分为两种。一种是ID Cache（ORM文档中称为二级Cache），用来存放Entity ID对应的Entity对象；一种是Query Cache，用来存放一条查询语句对应的查询结果集。
<br />
ID Cache非常直观，如同上述讲述的，一般是一个Entity Class对应一个Region，Entity存放到对应的Region里面。
<br />
Query Cache比较复杂，而且潜在作用很大，值得仔细讲解。
<br />
现有的ORM对Query Cache的支持并不是很理想。
<br />
比如，Hibernate把整个结果集直接放在Query Cache中。这样，有任何风吹草动，发生了任何数据库的写操作，Query Cache都需要清空。
<br />
有一种比较好的做法，把ID List存放在Query Cache中，每次获取的时候，先获取ID List，然后根据ID
List获取Entity List。Query Cache根据Query涉及到的Table Name来进行清理，一旦发生对这些Table
Name的修改操作，就可以根据不同情况，清理Query Cache。
<br />
比如，select t2.* from t1, t2 where t1.id = t2.foreign_id and t1.name = &#8216;a&#8217;
<br />
那么insert into t1, delete from t1, insert into t2, delete from t2都会清除这条Query Cache。
<br />
同样的 update t1 set name = &#8230; 这样的语句也会清除这条Query Cache。
<br />
Hibernate为什么不这么做，因为Query Cache的情况比较复杂。也许选择的结果集并不是只有一个Entity类型，也许只是几个字段。
<br />
这个地方，如果细分，还是有很多功夫可以做的。而且也很值得花功夫做，因为Query Cache对于性能的提高，有很大作用。
<br />
<br />
-----------------------------------------------------------
<br />
<br />
Query Key
<br />
Query Cache的性能需要考虑几个方面。比如，Query Key。Query Key一般由2个部分组成：Query String部分，SQL, HQL, EQL, or OQL；参数部分。
<br />
寻找Query Key的对应数据的时候，Query Key的比较有两个步骤，先hash，然后equals。所以，Query Key的hashcode和equals两个方法很重要。尤其是equals方法。
<br />
equals方法需要比较很长的Query String。如果没有命中，Query
String不相等，那么开销很小，因为通常来说，不相等的String长度都不同，或者前面的字符串都不相同。开销最大的是命中的时候，Query
String相等，那么需要把String从头比到尾。
<br />
我们可以采取一些方法来提高String的比较速度。比如，大部分的情况属于静态查询，我们可以采用Singleton
String。相同reference的String之间的比较速度很快。对于ORM来说，最好直接使用最外面的HQL, EQL,
OQL作为Query
Key，而不是采用生成的SQL结果。因为生成的SQL结果每次都是一个新String，具有不同的reference，Cache命中的时候，需要比较
整个字符串。
<br />
动态拼装的Query
String的性能提高比较难办。因为最终的结果，都是一个新String。我采用的一种方式是，动态拼装的结果是一个String[]。两个
String[]如果相等，那么里面的元素String的reference都是相等的，这是由JVM对一个Class内部的String常量进行优化的
结果。
<br />
比如，
<br />
String[] a = {
<br />
&nbsp; &#8220;select * from t where&#8221;
<br />
&nbsp; &#8220;a = 1&#8221;
<br />
&nbsp; &#8220;and b = 2&#8221;
<br />
};
<br />
<br />
String[] b = {
<br />
&nbsp; &#8220;select * from t where&#8221;
<br />
&nbsp; &#8220;a = 1&#8221;
<br />
&nbsp; &#8220;and b = 2&#8221;
<br />
};
<br />
<br />
那么a和b的比较只需要3次String reference的比较。
<img src ="http://www.blogjava.net/sway/aggbug/234404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:49 <a href="http://www.blogjava.net/sway/articles/234404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached全面剖析–5. memcached的应用和兼容程序</title><link>http://www.blogjava.net/sway/articles/234403.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:38:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234403.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234403.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234403.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234403.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234403.html</trackback:ping><description><![CDATA[<h1 class="contents_header">memcached全面剖析&#8211;5. memcached的应用和兼容程序</h1>
<div class="contents_info">作者:<a href="http://tech.idv2.com/" target="_blank">charlee</a>&nbsp;&nbsp;来源:<a href="http://tech.idv2.com/" target="_blank">idv2.com</a>&nbsp;&nbsp;时间:2008-09-28&nbsp;&nbsp;阅读:49 次&nbsp;&nbsp;<a href="http://tech.idv2.com/2008/07/31/memcached-005/" target="_blank">原文链接</a>&nbsp;&nbsp; <a class="graylink" href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://wz.cnblogs.com/create?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t)+'&amp;i=0','keyit','scrollbars=no,width=460,height=353,left=75,top=20,menu=yes,status=no,resizable=yes'));keyit.focus();">[收藏]</a>&nbsp;&nbsp; </div>
<!-- 页码和简介 -->
<div class="contents_main">
<div class="content_words" id="ArticleCnt" style="font-size: 14px">
<p>发表日：2008/7/30 <br />
作者：长野雅广(Masahiro Nagano) <br />
原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0005">http://gihyo.jp/dev/feature/01/memcached/0005</a></p>
<!-- end Pukiwiki generated code-->
<p>我是Mixi的长野。memcached的连载终于要结束了。到<a href="http://tech.idv2.com/2008/07/24/memcached-004/">上次</a>为止，我们介绍了与memcached直接相关的话题，本次介绍一些mixi的案例和实际应用上的话题，并介绍一些与memcached兼容的程序。 </p>
<!-- begin Pukiwiki generated code-->
<h2 id="content_2_0">mixi案例研究</h2>
<p>mixi在提供服务的初期阶段就使用了memcached。随着网站访问量的急剧增加，单纯为数据库添加slave已无法满足需要，因此引入了memcached。此外，我们也从增加可扩展性的方面进行了验证，证明了memcached的速度和稳定性都能满足需要。现在，memcached已成为mixi服务中非常重要的组成部分。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817263955.png" /><br />
</div>
<p>图1 现在的系统组件</p>
<h3 id="content_2_1">服务器配置和数量</h3>
<p>mixi使用了许许多多服务器，如数据库服务器、应用服务器、图片服务器、反向代理服务器等。单单memcached就有将近200台服务器在运行。 memcached服务器的典型配置如下：</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li>CPU：Intel Pentium 4 2.8GHz</li>
    <li>内存：4GB</li>
    <li>硬盘：146GB SCSI</li>
    <li>操作系统：Linux（x86_64）</li>
</ul>
<p>这些服务器以前曾用于数据库服务器等。随着CPU性能提升、内存价格下降，我们积极地将数据库服务器、应用服务器等换成了性能更强大、内存更多的服务器。这样，可以抑制mixi整体使用的服务器数量的急剧增加，降低管理成本。由于memcached服务器几乎不占用CPU，就将换下来的服务器用作memcached服务器了。</p>
<h3 id="content_2_2">memcached进程</h3>
<p>每台memcached服务器仅启动一个memcached进程。分配给memcached的内存为3GB，启动参数如下：</p>
<pre>/usr/bin/memcached -p 11211 -u nobody -m 3000 -c 30720</pre>
<p>由于使用了x86_64的操作系统，因此能分配2GB以上的内存。32位操作系统中，每个进程最多只能使用2GB内存。也曾经考虑过启动多个分配2GB以下内存的进程，但这样一台服务器上的TCP连接数就会成倍增加，管理上也变得复杂，所以mixi就统一使用了64位操作系统。</p>
<p>另外，虽然服务器的内存为4GB，却仅分配了3GB，是因为内存分配量超过这个值，就有可能导致内存交换(swap)。连载的<a href="http://tech.idv2.com/2008/07/11/memcached-002/">第2次</a>中前坂讲解过了memcached的内存存储&#8220;slab allocator&#8221;，当时说过，memcached启动时指定的内存分配量是memcached用于保存数据的量，没有包括&#8220;slab allocator&#8221;本身占用的内存、以及为了保存数据而设置的管理空间。因此，memcached进程的实际内存分配量要比指定的容量要大，这一点应当注意。</p>
<p>mixi保存在memcached中的数据大部分都比较小。这样，进程的大小要比指定的容量大很多。因此，我们反复改变内存分配量进行验证，确认了3GB的大小不会引发swap，这就是现在应用的数值。</p>
<h3 id="content_2_3">memcached使用方法和客户端</h3>
<p>现在，mixi的服务将200台左右的memcached服务器作为一个pool使用。每台服务器的容量为3GB，那么全体就有了将近600GB的巨大的内存数据库。客户端程序库使用了本连载中多次提到车的Cache::Memcached::Fast，与服务器进行交互。当然，缓存的分布式算法使用的是 <a href="http://tech.idv2.com/2008/07/24/memcached-004/">第4次</a>介绍过的 Consistent Hashing算法。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://search.cpan.org/dist/Cache-Memcached-Fast/">Cache::Memcached::Fast - search.cpan.org</a></li>
</ul>
<p>应用层上memcached的使用方法由开发应用程序的工程师自行决定并实现。但是，为了防止车轮再造、防止Cache::Memcached::Fast上的教训再次发生，我们提供了Cache::Memcached::Fast的wrap模块并使用。</p>
<h4 id="content_2_4">通过Cache::Memcached::Fast维持连接</h4>
<p>Cache::Memcached的情况下，与memcached的连接（文件句柄）保存在Cache::Memcached包内的类变量中。在mod_perl和FastCGI等环境下，包内的变量不会像CGI那样随时重新启动，而是在进程中一直保持。其结果就是不会断开与memcached的连接，减少了TCP连接建立时的开销，同时也能防止短时间内反复进行TCP连接、断开而导致的TCP端口资源枯竭。</p>
<p>但是，Cache::Memcached::Fast没有这个功能，所以需要在模块之外将Cache::Memcached::Fast对象保持在类变量中，以保证持久连接。</p>
<pre>package Gihyo::Memcached;<br />
<br />
use strict;<br />
use warnings;<br />
use Cache::Memcached::Fast;<br />
<br />
my @server_list = qw/192.168.1.1:11211 192.168.1.1:11211/;<br />
my $fast;  ## 用于保持对象<br />
<br />
sub new {<br />
my $self  = bless {}, shift;<br />
if ( !$fast ) {<br />
$fast = Cache::Memcached::Fast-&gt;new({ servers =&gt; \@server_list });<br />
}<br />
$self-&gt;{_fast} = $fast;<br />
return $self;<br />
}<br />
<br />
sub get {<br />
my $self = shift;<br />
$self-&gt;{_fast}-&gt;get(@_);<br />
}</pre>
<p>上面的例子中，Cache::Memcached::Fast对象保存到类变量$fast中。</p>
<h4 id="content_2_5">公共数据的处理和rehash</h4>
<p>诸如mixi的主页上的新闻这样的所有用户共享的缓存数据、设置信息等数据，会占用许多页，访问次数也非常多。在这种条件下，访问很容易集中到某台memcached服务器上。访问集中本身并不是问题，但是一旦访问集中的那台服务器发生故障导致memcached无法连接，就会产生巨大的问题。</p>
<p>连载的<a href="http://tech.idv2.com/2008/07/24/memcached-004/">第4次</a> 中提到，Cache::Memcached拥有rehash功能，即在无法连接保存数据的服务器的情况下，会再次计算hash值，连接其他的服务器。</p>
<p>但是，Cache::Memcached::Fast没有这个功能。不过，它能够在连接服务器失败时，短时间内不再连接该服务器的功能。</p>
<pre>my $fast = Cache::Memcached::Fast-&gt;new({<br />
max_failures     =&gt; 3,<br />
failure_timeout  =&gt; 1<br />
});</pre>
<p>在failure_timeout秒内发生max_failures以上次连接失败，就不再连接该memcached服务器。我们的设置是1秒钟3次以上。</p>
<p>此外，mixi还为所有用户共享的缓存数据的键名设置命名规则，符合命名规则的数据会自动保存到多台memcached服务器中，取得时从中仅选取一台服务器。创建该函数库后，就可以使memcached服务器故障不再产生其他影响。</p>
<h2 id="content_2_6">memcached应用经验</h2>
<p>到此为止介绍了memcached内部构造和函数库，接下来介绍一些其他的应用经验。</p>
<h3 id="content_2_7">通过daemontools启动</h3>
<p>通常情况下memcached运行得相当稳定，但mixi现在使用的最新版1.2.5 曾经发生过几次memcached进程死掉的情况。架构上保证了即使有几台memcached故障也不会影响服务，不过对于memcached进程死掉的服务器，只要重新启动memcached，就可以正常运行，所以采用了监视memcached进程并自动启动的方法。于是使用了daemontools。</p>
<p>daemontools是qmail的作者DJB开发的UNIX服务管理工具集，其中名为supervise的程序可用于服务启动、停止的服务重启等。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://cr.yp.to/daemontools.html">daemontools</a></li>
</ul>
<p>这里不介绍daemontools的安装了。mixi使用了以下的run脚本来启动memcached。</p>
<pre>#!/bin/sh<br />
<br />
if [ -f /etc/sysconfig/memcached ];then<br />
. /etc/sysconfig/memcached<br />
fi<br />
<br />
exec 2&gt;&amp;1<br />
exec /usr/bin/memcached -p $PORT -u $USER  -m $CACHESIZE -c $MAXCONN $OPTIONS</pre>
<h3 id="content_2_8">监视</h3>
<p>mixi使用了名为&#8220;nagios&#8221;的开源监视软件来监视memcached。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://www.nagios.org/">Nagios: Home</a></li>
</ul>
<p>在nagios中可以简单地开发插件，可以详细地监视memcached的get、add等动作。不过mixi仅通过stats命令来确认memcached的运行状态。</p>
<pre>define command {<br />
command_name                   check_memcached<br />
command_line                   $USER1$/check_tcp -H $HOSTADDRESS$ -p 11211 -t 5 -E -s 'stats\r\nquit\r\n' -e 'uptime' -M crit<br />
}</pre>
<p>此外，mixi将stats目录的结果通过rrdtool转化成图形，进行性能监视，并将每天的内存使用量做成报表，通过邮件与开发者共享。</p>
<h3 id="content_2_9">memcached的性能</h3>
<p>连载中已介绍过，memcached的性能十分优秀。我们来看看mixi的实际案例。这里介绍的图表是服务所使用的访问最为集中的memcached服务器。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817275633.png" /><br />
</div>
<p>图2 请求数</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817281265.png" /><br />
</div>
<p>图3 流量</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817282573.png" /><br />
</div>
<p>图4 TCP连接数</p>
<p>从上至下依次为请求数、流量和TCP连接数。请求数最大为15000qps，流量达到400Mbps，这时的连接数已超过了10000个。该服务器没有特别的硬件，就是开头介绍的普通的memcached服务器。此时的CPU利用率为：</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817284036.png" /><br />
</div>
<p>图5 CPU利用率</p>
<p>可见，仍然有idle的部分。因此，memcached的性能非常高，可以作为Web应用程序开发者放心地保存临时数据或缓存数据的地方。</p>
<h2 id="content_2_10">兼容应用程序</h2>
<p>memcached的实现和协议都十分简单，因此有很多与memcached兼容的实现。一些功能强大的扩展可以将memcached的内存数据写到磁盘上，实现数据的持久性和冗余。连载<a href="http://tech.idv2.com/2008/07/16/memcached-003/">第3次</a> 介绍过，以后的memcached的存储层将变成可扩展的（pluggable），逐渐支持这些功能。</p>
<p>这里介绍几个与memcached兼容的应用程序。</p>
<dl class="list1" style="padding-left: 16px; margin-left: 16px">
<dt>repcached</dt>
<dd>为memcached提供复制(replication)功能的patch。</dd>
<dt>Flared</dt>
<dd>存储到QDBM。同时实现了异步复制和fail over等功能。</dd>
<dt>memcachedb</dt>
<dd>存储到BerkleyDB。还实现了message queue。</dd>
<dt>Tokyo Tyrant</dt>
<dd>将数据存储到Tokyo Cabinet。不仅与memcached协议兼容，还能通过HTTP进行访问。</dd></dl>
<h3 id="content_2_11">Tokyo Tyrant案例</h3>
<p>mixi使用了上述兼容应用程序中的Tokyo Tyrant。Tokyo Tyrant是平林开发的 Tokyo Cabinet DBM的网络接口。它有自己的协议，但也拥有memcached兼容协议，也可以通过HTTP进行数据交换。Tokyo Cabinet虽然是一种将数据写到磁盘的实现，但速度相当快。</p>
<p>mixi并没有将Tokyo Tyrant作为缓存服务器，而是将它作为保存键值对组合的DBMS来使用。主要作为存储用户上次访问时间的数据库来使用。它与几乎所有的mixi服务都有关，每次用户访问页面时都要更新数据，因此负荷相当高。MySQL的处理十分笨重，单独使用memcached保存数据又有可能会丢失数据，所以引入了Tokyo Tyrant。但无需重新开发客户端，只需原封不动地使用Cache::Memcached::Fast即可，这也是优点之一。关于Tokyo Tyrant的详细信息，请参考本公司的开发blog。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://alpha.mixi.co.jp/blog/?p=166">mixi Engineers' Blog - Tokyo Tyrantによる耐高負荷DBの構築</a></li>
    <li><a href="http://alpha.mixi.co.jp/blog/?p=185">mixi Engineers' Blog - Tokyo (Cabinet|Tyrant)の新機能</a></li>
</ul>
<h2 id="content_2_12">总结</h2>
<p>到本次为止，&#8220;memcached全面剖析&#8221;系列就结束了。我们介绍了memcached的基础、内部结构、分散算法和应用等内容。读完后如果您能对memcached产生兴趣，就是我们的荣幸。关于mixi的系统、应用方面的信息，请参考本公司的<a href="http://alpha.mixi.co.jp/">开发blog</a>。感谢您的阅读。</p>
<p>为了方便阅读，现将原来的翻译结果打包成PDF文档，<a href="http://download.cnblogs.com/kb/memcached.zip">下载</a>。</p>
</div>
</div>
<img src ="http://www.blogjava.net/sway/aggbug/234403.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:38 <a href="http://www.blogjava.net/sway/articles/234403.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached全面剖析–4. memcached的分布式算法</title><link>http://www.blogjava.net/sway/articles/234402.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:37:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234402.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234402.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234402.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234402.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234402.html</trackback:ping><description><![CDATA[<h1 class="contents_header">memcached全面剖析&#8211;4. memcached的分布式算法</h1>
<div class="contents_info">作者:<a href="http://tech.idv2.com/" target="_blank">charlee</a>&nbsp;&nbsp;来源:<a href="http://tech.idv2.com/" target="_blank">idv2.com</a>&nbsp;&nbsp;时间:2008-09-28&nbsp;&nbsp;阅读:48 次&nbsp;&nbsp;<a href="http://tech.idv2.com/2008/07/24/memcached-004/" target="_blank">原文链接</a>&nbsp;&nbsp; <a class="graylink" href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://wz.cnblogs.com/create?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t)+'&amp;i=0','keyit','scrollbars=no,width=460,height=353,left=75,top=20,menu=yes,status=no,resizable=yes'));keyit.focus();">[收藏]</a>&nbsp;&nbsp; </div>
<!-- 页码和简介 -->
<div class="contents_main">
<div class="content_words" id="ArticleCnt" style="font-size: 14px">
<p>发表日：2008/7/23 <br />
作者：长野雅广(Masahiro Nagano) <br />
原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0004">http://gihyo.jp/dev/feature/01/memcached/0004</a></p>
<!-- end Pukiwiki generated code-->
<p>我是Mixi的长野。 <a href="http://tech.idv2.com/2008/07/11/memcached-002/">第2次</a>、 <a href="http://tech.idv2.com/2008/07/16/memcached-003/">第3次</a> 由前坂介绍了memcached的内部情况。本次不再介绍memcached的内部结构，开始介绍memcached的分布式。</p>
<!-- begin Pukiwiki generated code-->
<h2 id="content_2_0">memcached的分布式</h2>
<p>正如<a href="http://tech.idv2.com/2008/07/10/memcached-001/">第1次</a>中介绍的那样， memcached虽然称为&#8220;分布式&#8221;缓存服务器，但服务器端并没有&#8220;分布式&#8221;功能。服务器端仅包括 <a href="http://tech.idv2.com/2008/07/11/memcached-002/">第2次</a>、 <a href="http://tech.idv2.com/2008/07/16/memcached-003/">第3次</a> 前坂介绍的内存存储功能，其实现非常简单。至于memcached的分布式，则是完全由客户端程序库实现的。这种分布式是memcached的最大特点。</p>
<h3 id="content_2_1">memcached的分布式是什么意思？</h3>
<p>这里多次使用了&#8220;分布式&#8221;这个词，但并未做详细解释。现在开始简单地介绍一下其原理，各个客户端的实现基本相同。</p>
<p>下面假设memcached服务器有node1～node3三台，应用程序要保存键名为&#8220;tokyo&#8221;&#8220;kanagawa&#8221;&#8220;chiba&#8221;&#8220;saitama&#8221;&#8220;gunma&#8221; 的数据。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817111437.png" /><br />
</div>
<p>图1 分布式简介：准备</p>
<p>首先向memcached中添加&#8220;tokyo&#8221;。将&#8220;tokyo&#8221;传给客户端程序库后，客户端实现的算法就会根据&#8220;键&#8221;来决定保存数据的memcached服务器。服务器选定后，即命令它保存&#8220;tokyo&#8221;及其值。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817114280.png" /><br />
</div>
<p>图2 分布式简介：添加时</p>
<p>同样，&#8220;kanagawa&#8221;&#8220;chiba&#8221;&#8220;saitama&#8221;&#8220;gunma&#8221;都是先选择服务器再保存。</p>
<p>接下来获取保存的数据。获取时也要将要获取的键&#8220;tokyo&#8221;传递给函数库。函数库通过与数据保存时相同的算法，根据&#8220;键&#8221;选择服务器。使用的算法相同，就能选中与保存时相同的服务器，然后发送get命令。只要数据没有因为某些原因被删除，就能获得保存的值。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817121928.png" /><br />
</div>
<p>图3 分布式简介：获取时</p>
<p>这样，将不同的键保存到不同的服务器上，就实现了memcached的分布式。 memcached服务器增多后，键就会分散，即使一台memcached服务器发生故障无法连接，也不会影响其他的缓存，系统依然能继续运行。</p>
<p>接下来介绍<a href="http://tech.idv2.com/2008/07/10/memcached-001/">第1次</a> 中提到的Perl客户端函数库Cache::Memcached实现的分布式方法。</p>
<h2 id="content_2_2">Cache::Memcached的分布式方法</h2>
<p>Perl的memcached客户端函数库Cache::Memcached是 memcached的作者Brad Fitzpatrick的作品，可以说是原装的函数库了。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://search.cpan.org/dist/Cache-Memcached/">Cache::Memcached - search.cpan.org</a></li>
</ul>
<p>该函数库实现了分布式功能，是memcached标准的分布式方法。</p>
<h3 id="content_2_3">根据余数计算分散</h3>
<p>Cache::Memcached的分布式方法简单来说，就是&#8220;根据服务器台数的余数进行分散&#8221;。求得键的整数哈希值，再除以服务器台数，根据其余数来选择服务器。</p>
<p>下面将Cache::Memcached简化成以下的Perl脚本来进行说明。</p>
<pre>use strict;<br />
use warnings;<br />
use String::CRC32;<br />
<br />
my @nodes = ('node1','node2','node3');<br />
my @keys = ('tokyo', 'kanagawa', 'chiba', 'saitama', 'gunma');<br />
<br />
foreach my $key (@keys) {<br />
my $crc = crc32($key);             # CRC値<br />
my $mod = $crc % ( $#nodes + 1 );<br />
my $server = $nodes[ $mod ];       # 根据余数选择服务器<br />
printf "%s =&gt; %s\n", $key, $server;<br />
}</pre>
<p>Cache::Memcached在求哈希值时使用了CRC。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://search.cpan.org/dist/String-CRC32/">String::CRC32 - search.cpan.org</a></li>
</ul>
<p>首先求得字符串的CRC值，根据该值除以服务器节点数目得到的余数决定服务器。上面的代码执行后输入以下结果：</p>
<pre>tokyo       =&gt; node2<br />
kanagawa =&gt; node3<br />
chiba       =&gt; node2<br />
saitama   =&gt; node1<br />
gunma     =&gt; node1</pre>
<p>根据该结果，&#8220;tokyo&#8221;分散到node2，&#8220;kanagawa&#8221;分散到node3等。多说一句，当选择的服务器无法连接时，Cache::Memcached会将连接次数添加到键之后，再次计算哈希值并尝试连接。这个动作称为rehash。不希望rehash时可以在生成Cache::Memcached对象时指定&#8220;rehash =&gt; 0&#8221;选项。</p>
<h3 id="content_2_4">根据余数计算分散的缺点</h3>
<p>余数计算的方法简单，数据的分散性也相当优秀，但也有其缺点。那就是当添加或移除服务器时，缓存重组的代价相当巨大。添加服务器后，余数就会产生巨变，这样就无法获取与保存时相同的服务器，从而影响缓存的命中率。用Perl写段代码来验证其代价。</p>
<pre>use strict;<br />
use warnings;<br />
use String::CRC32;<br />
<br />
my @nodes = @ARGV;<br />
my @keys = ('a'..'z');<br />
my %nodes;<br />
<br />
foreach my $key ( @keys ) {<br />
my $hash = crc32($key);<br />
my $mod = $hash % ( $#nodes + 1 );<br />
my $server = $nodes[ $mod ];<br />
push @{ $nodes{ $server } }, $key;<br />
}<br />
<br />
foreach my $node ( sort keys %nodes ) {<br />
printf "%s: %s\n", $node,  join ",", @{ $nodes{$node} };<br />
}</pre>
<p>这段Perl脚本演示了将&#8220;a&#8221;到&#8220;z&#8221;的键保存到memcached并访问的情况。将其保存为mod.pl并执行。</p>
<p>首先，当服务器只有三台时：</p>
<pre>$ mod.pl node1 node2 nod3<br />
node1: a,c,d,e,h,j,n,u,w,x<br />
node2: g,i,k,l,p,r,s,y<br />
node3: b,f,m,o,q,t,v,z</pre>
<p>结果如上，node1保存a、c、d、e&#8230;&#8230;，node2保存g、i、k&#8230;&#8230;，每台服务器都保存了8个到10个数据。</p>
<p>接下来增加一台memcached服务器。</p>
<pre>$ mod.pl node1 node2 node3 node4<br />
node1: d,f,m,o,t,v<br />
node2: b,i,k,p,r,y<br />
node3: e,g,l,n,u,w<br />
node4: a,c,h,j,q,s,x,z</pre>
<p>添加了node4。可见，只有d、i、k、p、r、y命中了。像这样，添加节点后键分散到的服务器会发生巨大变化。26个键中只有六个在访问原来的服务器，其他的全都移到了其他服务器。命中率降低到23%。在Web应用程序中使用memcached时，在添加memcached服务器的瞬间缓存效率会大幅度下降，负载会集中到数据库服务器上，有可能会发生无法提供正常服务的情况。</p>
<p>mixi的Web应用程序运用中也有这个问题，导致无法添加memcached服务器。但由于使用了新的分布式方法，现在可以轻而易举地添加memcached服务器了。这种分布式方法称为 Consistent Hashing。</p>
<h2 id="content_2_5">Consistent Hashing</h2>
<p>关于Consistent Hashing的思想，mixi株式会社的开发blog等许多地方都介绍过，这里只简单地说明一下。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://alpha.mixi.co.jp/blog/?p=158">mixi Engineers' Blog - スマートな分散で快適キャッシュライフ </a></li>
    <li><a href="http://www.hyuki.com/yukiwiki/wiki.cgi?ConsistentHashing">ConsistentHashing - コンシステント ハッシュ法</a></li>
</ul>
<h3 id="content_2_6">Consistent Hashing的简单说明</h3>
<p>Consistent Hashing如下所示：首先求出memcached服务器（节点）的哈希值，并将其配置到0～2<sup>32</sup>的圆（continuum）上。然后用同样的方法求出存储数据的键的哈希值，并映射到圆上。然后从数据映射到的位置开始顺时针查找，将数据保存到找到的第一个服务器上。如果超过2<sup>32</sup>仍然找不到服务器，就会保存到第一台memcached服务器上。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817125369.png" /><br />
</div>
<p>图4 Consistent Hashing：基本原理</p>
<p>从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率，但Consistent Hashing中，只有在continuum上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响。</p>
<div class="img_margin" style="text-align: left"><img alt="" src="http://pic001.cnblogs.com/img/dudu/200809/2008092817131010.png" /><br />
</div>
<p>图5 Consistent Hashing：添加服务器</p>
<p>因此，Consistent Hashing最大限度地抑制了键的重新分布。而且，有的Consistent Hashing的实现方法还采用了虚拟节点的思想。使用一般的hash函数的话，服务器的映射地点的分布非常不均匀。因此，使用虚拟节点的思想，为每个物理节点（服务器）在continuum上分配100～200个点。这样就能抑制分布不均匀，最大限度地减小服务器增减时的缓存重新分布。</p>
<p>通过下文中介绍的使用Consistent Hashing算法的memcached客户端函数库进行测试的结果是，由服务器台数（n）和增加的服务器台数（m）计算增加服务器后的命中率计算公式如下：</p>
<p>(1 - n/(n+m)) * 100</p>
<h3 id="content_2_7">支持Consistent Hashing的函数库</h3>
<p>本连载中多次介绍的Cache::Memcached虽然不支持Consistent Hashing，但已有几个客户端函数库支持了这种新的分布式算法。第一个支持Consistent Hashing和虚拟节点的memcached客户端函数库是名为libketama的PHP库，由last.fm开发。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://www.lastfm.jp/user/RJ/journal/2007/04/10/rz_libketama_-_a_consistent_hashing_algo_for_memcache_clients">libketama - a consistent hashing algo for memcache clients &#8211; RJ ブログ - Users at Last.fm </a></li>
</ul>
<p>至于Perl客户端，连载的<a href="http://tech.idv2.com/2008/07/11/memcached-001/">第1次</a> 中介绍过的Cache::Memcached::Fast和Cache::Memcached::libmemcached支持 Consistent Hashing。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://search.cpan.org/dist/Cache-Memcached-Fast/">Cache::Memcached::Fast - search.cpan.org </a></li>
    <li><a href="http://search.cpan.org/dist/Cache-Memcached-libmemcached/">Cache::Memcached::libmemcached - search.cpan.org </a></li>
</ul>
<p>两者的接口都与Cache::Memcached几乎相同，如果正在使用Cache::Memcached，那么就可以方便地替换过来。Cache::Memcached::Fast重新实现了libketama，使用Consistent Hashing创建对象时可以指定ketama_points选项。</p>
<pre>my $memcached = Cache::Memcached::Fast-&gt;new({<br />
servers =&gt; ["192.168.0.1:11211","192.168.0.2:11211"],<br />
ketama_points =&gt; 150<br />
});</pre>
<p>另外，Cache::Memcached::libmemcached 是一个使用了Brain Aker开发的C函数库libmemcached的Perl模块。 libmemcached本身支持几种分布式算法，也支持Consistent Hashing，其Perl绑定也支持Consistent Hashing。</p>
<ul class="list1" style="padding-left: 16px; margin-left: 16px">
    <li><a href="http://tangent.org/552/libmemcached.html">Tangent Software: libmemcached </a></li>
</ul>
<h2 id="content_2_8">总结</h2>
<p>本次介绍了memcached的分布式算法，主要有memcached的分布式是由客户端函数库实现，以及高效率地分散数据的Consistent Hashing算法。下次将介绍mixi在memcached应用方面的一些经验，和相关的兼容应用程序。</p>
</div>
</div>
<img src ="http://www.blogjava.net/sway/aggbug/234402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:37 <a href="http://www.blogjava.net/sway/articles/234402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached全面剖析–3.memcached的删除机制和发展方向</title><link>http://www.blogjava.net/sway/articles/234401.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234401.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234401.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234401.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234401.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234401.html</trackback:ping><description><![CDATA[<h1>memcached全面剖析&#8211;3.memcached的删除机制和发展方向</h1>
<div>
作者:<a href="http://tech.idv2.com/" target="_blank">charlee</a>&nbsp;&nbsp;来源:<a href="http://tech.idv2.com/" target="_blank">idv2.com</a>&nbsp;&nbsp;时间:2008-09-28&nbsp;&nbsp;阅读:44 次&nbsp;&nbsp;<a href="http://tech.idv2.com/2008/07/16/memcached-003/" target="_blank">原文链接</a>&nbsp;&nbsp;
<a href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://wz.cnblogs.com/create?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t)+'&amp;i=0','keyit','scrollbars=no,width=460,height=353,left=75,top=20,menu=yes,status=no,resizable=yes'));keyit.focus();" class="graylink">[收藏]</a>&nbsp;&nbsp;
</div>
<div style="font-size: 14px;" id="ArticleCnt" class="content_words">
<p>下面是《memcached全面剖析》的第三部分。</p>
<p>发表日：2008/7/16 <br />
作者：前坂徹(Toru Maesaka) <br />
原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0003">http://gihyo.jp/dev/feature/01/memcached/0003</a></p>
<p>memcached是缓存，所以数据不会永久保存在服务器上，这是向系统中引入memcached的前提。
本次介绍memcached的数据删除机制，以及memcached的最新发展方向——二进制协议（Binary Protocol）
和外部引擎支持。</p>
<h2 id="content_2_0">memcached在数据删除方面有效利用资源</h2>
<h3 id="content_2_1">数据不会真正从memcached中消失</h3>
<p><a href="http://tech.idv2.com/2008/07/11/memcached-002/">上次</a>介绍过，
memcached不会释放已分配的内存。记录超时后，客户端就无法再看见该记录（invisible，透明），
其存储空间即可重复使用。</p>
<h3 id="content_2_2">Lazy Expiration</h3>
<p>memcached内部不会监视记录是否过期，而是在get时查看记录的时间戳，检查记录是否过期。
这种技术被称为lazy（惰性）expiration。因此，memcached不会在过期监视上耗费CPU时间。</p>
<h2 id="content_2_3">LRU：从缓存中有效删除数据的原理</h2>
<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>$ memcached -M -m 1024</pre>
<p>启动时必须注意的是，小写的&#8220;-m&#8221;选项是用来指定最大内存大小的。不指定具体数值则使用默认值64MB。</p>
<p>指定&#8220;-M&#8221;参数启动后，内存用尽时memcached会返回错误。
话说回来，memcached毕竟不是存储器，而是缓存，所以推荐使用LRU。</p>
<h2 id="content_2_4">memcached的最新发展方向</h2>
<p>memcached的roadmap上有两个大的目标。一个是二进制协议的策划和实现，另一个是外部引擎的加载功能。</p>
<h3 id="content_2_5">关于二进制协议</h3>
<p>使用二进制协议的理由是它不需要文本协议的解析处理，使得原本高速的memcached的性能更上一层楼，
还能减少文本协议的漏洞。目前已大部分实现，开发用的代码库中已包含了该功能。
memcached的下载页面上有代码库的链接。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li><a href="http://danga.com/memcached/download.bml">http://danga.com/memcached/download.bml</a></li>
</ul>
<h3 id="content_2_6">二进制协议的格式</h3>
<p>协议的包为24字节的帧，其后面是键和无结构数据（Unstructured Data）。
实际的格式如下（引自协议文档）：</p>
<pre> Byte/     0       |       1       |       2       |       3       |   <br />
/              |               |               |               |   <br />
|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|<br />
+---------------+---------------+---------------+---------------+<br />
0/ HEADER                                                        /   <br />
/                                                               /   <br />
/                                                               /   <br />
/                                                               /   <br />
+---------------+---------------+---------------+---------------+<br />
24/ COMMAND-SPECIFIC EXTRAS (as needed)                           /   <br />
+/  (note length in th extras length header field)               /   <br />
+---------------+---------------+---------------+---------------+<br />
m/ Key (as needed)                                               /   <br />
+/  (note length in key length header field)                     /   <br />
+---------------+---------------+---------------+---------------+<br />
n/ Value (as needed)                                             /   <br />
+/  (note length is total body length header field, minus        /   <br />
+/   sum of the extras and key length body fields)               /   <br />
+---------------+---------------+---------------+---------------+<br />
Total 24 bytes</pre>
<p>如上所示，包格式十分简单。需要注意的是，占据了16字节的头部(HEADER)分为
请求头（Request Header）和响应头（Response Header）两种。
头部中包含了表示包的有效性的Magic字节、命令种类、键长度、值长度等信息，格式如下：</p>
<pre>Request Header<br />
<br />
Byte/     0       |       1       |       2       |       3       |<br />
/              |               |               |               |<br />
|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|<br />
+---------------+---------------+---------------+---------------+<br />
0| Magic         | Opcode        | Key length                    |<br />
+---------------+---------------+---------------+---------------+<br />
4| Extras length | Data type     | Reserved                      |<br />
+---------------+---------------+---------------+---------------+<br />
8| Total body length                                             |<br />
+---------------+---------------+---------------+---------------+<br />
12| Opaque                                                        |<br />
+---------------+---------------+---------------+---------------+<br />
16| CAS                                                           |<br />
|                                                               |<br />
+---------------+---------------+---------------+---------------+</pre>
<pre>Response Header<br />
<br />
Byte/     0       |       1       |       2       |       3       |<br />
/              |               |               |               |<br />
|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|<br />
+---------------+---------------+---------------+---------------+<br />
0| Magic         | Opcode        | Key Length                    |<br />
+---------------+---------------+---------------+---------------+<br />
4| Extras length | Data type     | Status                        |<br />
+---------------+---------------+---------------+---------------+<br />
8| Total body length                                             |<br />
+---------------+---------------+---------------+---------------+<br />
12| Opaque                                                        |<br />
+---------------+---------------+---------------+---------------+<br />
16| CAS                                                           |<br />
|                                                               |<br />
+---------------+---------------+---------------+---------------+</pre>
<p>如希望了解各个部分的详细内容，可以checkout出memcached的二进制协议的代码树，
参考其中的docs文件夹中的protocol_binary.txt文档。</p>
<h3 id="content_2_7">HEADER中引人注目的地方</h3>
<p>看到HEADER格式后我的感想是，键的上限太大了！现在的memcached规格中，键长度最大为250字节，
但二进制协议中键的大小用2字节表示。因此，理论上最大可使用65536字节（2&lt;sup&gt;16&lt;/sup&gt;）长的键。
尽管250字节以上的键并不会太常用，二进制协议发布之后就可以使用巨大的键了。</p>
<p>二进制协议从下一版本1.3系列开始支持。</p>
<h2 id="content_2_8">外部引擎支持</h2>
<p>我去年曾经试验性地将memcached的存储层改造成了可扩展的（pluggable）。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <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>
<h3 id="content_2_9">外部引擎支持的必要性</h3>
<p>世界上有许多memcached的派生软件，其理由是希望永久保存数据、实现数据冗余等，
即使牺牲一些性能也在所不惜。我在开发memcached之前，在mixi的研发部也曾经
考虑过重新发明memcached。</p>
<p>外部引擎的加载机制能封装memcached的网络功能、事件处理等复杂的处理。
因此，现阶段通过强制手段或重新设计等方式使memcached和存储引擎合作的困难
就会烟消云散，尝试各种引擎就会变得轻而易举了。</p>
<h3 id="content_2_10">简单API设计的成功的关键</h3>
<p>该项目中我们最重视的是API设计。函数过多，会使引擎开发者感到麻烦；
过于复杂，实现引擎的门槛就会过高。因此，最初版本的接口函数只有13个。
具体内容限于篇幅，这里就省略了，仅说明一下引擎应当完成的操作：</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <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>
<h3 id="content_2_11">重新审视现在的体系</h3>
<p>memcached支持外部存储的难点是，网络和事件处理相关的代码（核心服务器）与
内存存储的代码紧密关联。这种现象也称为tightly coupled（紧密耦合）。
必须将内存存储的代码从核心服务器中独立出来，才能灵活地支持外部引擎。
因此，基于我们设计的API，memcached被重构成下面的样子：</p>
<div style="text-align: left;"><img src="http://pic001.cnblogs.com/img/dudu/200809/2008092817035014.png" alt="" /><br />
</div>
<p>重构之后，我们与1.2.5版、二进制协议支持版等进行了性能对比，证实了它不会造成性能影响。</p>
<p>在考虑如何支持外部引擎加载时，让memcached进行并行控制（concurrency control）的方案是最为容易的，
但是对于引擎而言，并行控制正是性能的真谛，因此我们采用了将多线程支持完全交给引擎的设计方案。</p>
<p>以后的改进，会使得memcached的应用范围更为广泛。</p>
<h2 id="content_2_12">总结</h2>
<p>本次介绍了memcached的超时原理、内部如何删除数据等，在此之上又介绍了二进制协议和
外部引擎支持等memcached的最新发展方向。这些功能要到1.3版才会支持，敬请期待！</p>
<p>这是我在本连载中的最后一篇。感谢大家阅读我的文章！</p>
<p>下次由长野来介绍memcached的应用知识和应用程序兼容性等内容。</p>
</div>
<img src ="http://www.blogjava.net/sway/aggbug/234401.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:32 <a href="http://www.blogjava.net/sway/articles/234401.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached全面剖析–2.理解memcached的内存存储</title><link>http://www.blogjava.net/sway/articles/234400.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:31:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234400.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234400.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234400.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234400.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234400.html</trackback:ping><description><![CDATA[<h1>memcached全面剖析&#8211;2.理解memcached的内存存储</h1>
<div>
作者:<a href="http://tech.idv2.com/" target="_blank">charlee</a>&nbsp;&nbsp;来源:<a href="http://tech.idv2.com/" target="_blank">idv2.com</a>&nbsp;&nbsp;时间:2008-09-28&nbsp;&nbsp;阅读:57 次&nbsp;&nbsp;<a href="http://tech.idv2.com/2008/07/11/memcached-002/" target="_blank">原文链接</a>&nbsp;&nbsp;
<a href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://wz.cnblogs.com/create?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t)+'&amp;i=0','keyit','scrollbars=no,width=460,height=353,left=75,top=20,menu=yes,status=no,resizable=yes'));keyit.focus();" class="graylink">[收藏]</a>&nbsp;&nbsp;
</div>
<div style="font-size: 14px;" id="ArticleCnt" class="content_words">
<p>下面是《memcached全面剖析》的第二部分。</p>
<p>发表日：2008/7/9 <br />
作者：前坂徹(Toru Maesaka) <br />
原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0002">http://gihyo.jp/dev/feature/01/memcached/0002</a></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>
<h2 id="content_2_0">Slab Allocation机制：整理内存以便重复使用</h2>
<p>最近的memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。
在该机制出现以前，内存的分配是通过对所有记录简单地进行malloc和free来进行的。
但是，这种方式会导致内存碎片，加重操作系统内存管理器的负担，最坏的情况下，
会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。</p>
<p>下面来看看Slab Allocator的原理。下面是memcached文档中的slab allocator的目标：</p>
<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>
<p>也就是说，Slab Allocator的基本原理是按照预先规定的大小，将分配的内存分割成特定长度的块，
以完全解决内存碎片问题。</p>
<p>Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块（chunk），
并把尺寸相同的块分成组（chunk的集合）（图1）。</p>
<div style="text-align: left;"><img src="http://pic001.cnblogs.com/img/dudu/200809/2008092816564538.png" alt="" /><br />
</div>
<p>图1 Slab Allocation的构造图</p>
<p>而且，slab allocator还有重复使用已分配的内存的目的。
也就是说，分配到的内存不会释放，而是重复利用。</p>
<h3 id="content_2_1">Slab Allocation的主要术语</h3>
<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>
<h2 id="content_2_2">在Slab中缓存记录的原理</h2>
<p>下面说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。</p>
<p>memcached根据收到的数据的大小，选择最适合数据大小的slab（图2）。
memcached中保存着slab内空闲chunk的列表，根据该列表选择chunk，
然后将数据缓存于其中。</p>
<div style="text-align: left;"><img src="http://pic001.cnblogs.com/img/dudu/200809/2008092816571451.png" alt="" /><br />
</div>
<p>图2 选择存储记录的组的方法</p>
<p>实际上，Slab Allocator也是有利也有弊。下面介绍一下它的缺点。</p>
<h2 id="content_2_3">Slab Allocator的缺点</h2>
<p>Slab Allocator解决了当初的内存碎片问题，但新的机制也给memcached带来了新的问题。</p>
<p>这个问题就是，由于分配的是特定长度的内存，因此无法有效利用分配的内存。
例如，将100字节的数据缓存到128字节的chunk中，剩余的28字节就浪费了（图3）。</p>
<div style="text-align: left;"><img src="http://pic001.cnblogs.com/img/dudu/200809/2008092816574192.png" alt="" /><br />
</div>
<p>图3 chunk空间的使用</p>
<p>对于该问题目前还没有完美的解决方案，但在文档中记载了比较有效的解决方案。</p>
<p>The most efficient way to reduce the waste is to use a list of size
classes that closely matches (if that's at all possible) common sizes
of objects that the clients of this particular installation of
memcached are likely to store.</p>
<p>就是说，如果预先知道客户端发送的数据的公用大小，或者仅缓存大小相同的数据的情况下，
只要使用适合数据大小的组的列表，就可以减少浪费。</p>
<p>但是很遗憾，现在还不能进行任何调优，只能期待以后的版本了。
但是，我们可以调节slab class的大小的差别。
接下来说明growth factor选项。</p>
<h2 id="content_2_4">使用Growth Factor进行调优</h2>
<p>memcached在启动时指定 Growth Factor因子（通过-f选项），
就可以在某种程度上控制slab之间的差异。默认值为1.25。
但是，在该选项出现之前，这个因子曾经固定为2，称为&#8220;powers of 2&#8221;策略。</p>
<p>让我们用以前的设置，以verbose模式启动memcached试试看：</p>
<pre>$ memcached -f 2 -vv</pre>
<p>下面是启动后的verbose输出：</p>
<pre>slab class   1: chunk size    128 perslab  8192<br />
slab class   2: chunk size    256 perslab  4096<br />
slab class   3: chunk size    512 perslab  2048<br />
slab class   4: chunk size   1024 perslab  1024<br />
slab class   5: chunk size   2048 perslab   512<br />
slab class   6: chunk size   4096 perslab   256<br />
slab class   7: chunk size   8192 perslab   128<br />
slab class   8: chunk size  16384 perslab    64<br />
slab class   9: chunk size  32768 perslab    32<br />
slab class  10: chunk size  65536 perslab    16<br />
slab class  11: chunk size 131072 perslab     8<br />
slab class  12: chunk size 262144 perslab     4<br />
slab class  13: chunk size 524288 perslab     2</pre>
<p>可见，从128字节的组开始，组的大小依次增大为原来的2倍。
这样设置的问题是，slab之间的差别比较大，有些情况下就相当浪费内存。
因此，为尽量减少内存浪费，两年前追加了growth factor这个选项。</p>
<p>来看看现在的默认设置（f=1.25）时的输出（篇幅所限，这里只写到第10组）：</p>
<pre>slab class   1: chunk size     88 perslab 11915<br />
slab class   2: chunk size    112 perslab  9362<br />
slab class   3: chunk size    144 perslab  7281<br />
slab class   4: chunk size    184 perslab  5698<br />
slab class   5: chunk size    232 perslab  4519<br />
slab class   6: chunk size    296 perslab  3542<br />
slab class   7: chunk size    376 perslab  2788<br />
slab class   8: chunk size    472 perslab  2221<br />
slab class   9: chunk size    592 perslab  1771<br />
slab class  10: chunk size    744 perslab  1409</pre>
<p>可见，组间差距比因子为2时小得多，更适合缓存几百字节的记录。
从上面的输出结果来看，可能会觉得有些计算误差，
这些误差是为了保持字节数的对齐而故意设置的。</p>
<p>将memcached引入产品，或是直接使用默认值进行部署时，
最好是重新计算一下数据的预期平均长度，调整growth factor，
以获得最恰当的设置。内存是珍贵的资源，浪费就太可惜了。</p>
<p>接下来介绍一下如何使用memcached的stats命令查看slabs的利用率等各种各样的信息。</p>
<h2 id="content_2_5">查看memcached的内部状态</h2>
<p>memcached有个名为stats的命令，使用它可以获得各种各样的信息。
执行命令的方法很多，用telnet最为简单：</p>
<pre>$ telnet 主机名 端口号</pre>
<p>连接到memcached之后，输入stats再按回车，即可获得包括资源利用率在内的各种信息。
此外，输入"stats slabs"或"stats items"还可以获得关于缓存记录的信息。
结束程序请输入quit。</p>
<p>这些命令的详细信息可以参考memcached软件包内的protocol.txt文档。</p>
<pre>$ telnet localhost 11211<br />
Trying ::1...<br />
Connected to localhost.<br />
Escape character is '^]'.<br />
stats<br />
STAT pid 481<br />
STAT uptime 16574<br />
STAT time 1213687612<br />
STAT version 1.2.5<br />
STAT pointer_size 32<br />
STAT rusage_user 0.102297<br />
STAT rusage_system 0.214317<br />
STAT curr_items 0<br />
STAT total_items 0<br />
STAT bytes 0<br />
STAT curr_connections 6<br />
STAT total_connections 8<br />
STAT connection_structures 7<br />
STAT cmd_get 0<br />
STAT cmd_set 0<br />
STAT get_hits 0<br />
STAT get_misses 0<br />
STAT evictions 0<br />
STAT bytes_read 20<br />
STAT bytes_written 465<br />
STAT limit_maxbytes 67108864<br />
STAT threads 4<br />
END<br />
quit</pre>
<p>另外，如果安装了libmemcached这个面向C/C++语言的客户端库，就会安装 memstat 这个命令。
使用方法很简单，可以用更少的步骤获得与telnet相同的信息，还能一次性从多台服务器获得信息。</p>
<pre>$ memstat --servers=server1,server2,server3,...</pre>
<p>libmemcached可以从下面的地址获得：</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li><a href="http://tangent.org/552/libmemcached.html">http://tangent.org/552/libmemcached.html</a></li>
</ul>
<h2 id="content_2_6">查看slabs的使用状况</h2>
<p>使用memcached的创造着Brad写的名为memcached-tool的Perl脚本，可以方便地获得slab的使用情况
（它将memcached的返回值整理成容易阅读的格式）。可以从下面的地址获得脚本：</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li><a href="http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool">http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool</a></li>
</ul>
<p>使用方法也极其简单：</p>
<pre>$ memcached-tool 主机名:端口 选项</pre>
<p>查看slabs使用状况时无需指定选项，因此用下面的命令即可：</p>
<pre>$ memcached-tool 主机名:端口</pre>
<p>获得的信息如下所示：</p>
<pre> #  Item_Size   Max_age  1MB_pages Count   Full?<br />
1     104 B  1394292 s    1215 12249628    yes<br />
2     136 B  1456795 s      52  400919     yes<br />
3     176 B  1339587 s      33  196567     yes<br />
4     224 B  1360926 s     109  510221     yes<br />
5     280 B  1570071 s      49  183452     yes<br />
6     352 B  1592051 s      77  229197     yes<br />
7     440 B  1517732 s      66  157183     yes<br />
8     552 B  1460821 s      62  117697     yes<br />
9     696 B  1521917 s     143  215308     yes<br />
10     872 B  1695035 s     205  246162     yes<br />
11     1.1 kB 1681650 s     233  221968     yes<br />
12     1.3 kB 1603363 s     241  183621     yes<br />
13     1.7 kB 1634218 s      94   57197     yes<br />
14     2.1 kB 1695038 s      75   36488     yes<br />
15     2.6 kB 1747075 s      65   25203     yes<br />
16     3.3 kB 1760661 s      78   24167     yes</pre>
<p>各列的含义为：</p>
<div>
<table border="0" cellspacing="1">
    <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>
</div>
<p>从这个脚本获得的信息对于调优非常方便，强烈推荐使用。</p>
<h2 id="content_2_7">内存存储的总结</h2>
<p>本次简单说明了memcached的缓存机制和调优方法。
希望读者能理解memcached的内存管理原理及其优缺点。</p>
<p>下次将继续说明LRU和Expire等原理，以及memcached的最新发展方向——
可扩充体系（pluggable architecher））。</p>
</div>
<img src ="http://www.blogjava.net/sway/aggbug/234400.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:31 <a href="http://www.blogjava.net/sway/articles/234400.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>memcached完全剖析–1. memcached的基础</title><link>http://www.blogjava.net/sway/articles/234399.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 03:28:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234399.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234399.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234399.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234399.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234399.html</trackback:ping><description><![CDATA[<h1>memcached完全剖析&#8211;1. memcached的基础</h1>
<div>
作者:<a href="http://tech.idv2.com/" target="_blank">charlee</a>&nbsp;&nbsp;来源:<a href="http://tech.idv2.com/" target="_blank">idv2.com</a>&nbsp;&nbsp;时间:2008-09-28&nbsp;&nbsp;阅读:191 次&nbsp;&nbsp;<a href="http://tech.idv2.com/2008/07/10/memcached-001/" target="_blank">原文链接</a>&nbsp;&nbsp;
<a href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://wz.cnblogs.com/create?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t)+'&amp;i=0','keyit','scrollbars=no,width=460,height=353,left=75,top=20,menu=yes,status=no,resizable=yes'));keyit.focus();" class="graylink">[收藏]</a>&nbsp;&nbsp;
</div>
<div style="font-size: 14px;" id="ArticleCnt" class="content_words">
<p>翻译一篇技术评论社的文章，是讲memcached的连载。<a href="http://www.fcicq.net/wp/">fcicq</a>同学说这个东西很有用，希望大家喜欢。</p>
<p>发表日：2008/7/2 <br />
作者：长野雅广(Masahiro Nagano) <br />
原文链接：<a href="http://gihyo.jp/dev/feature/01/memcached/0001">http://gihyo.jp/dev/feature/01/memcached/0001</a></p>
<p>我是<a href="http://mixi.jp/">mixi株式会社</a>开发部系统运营组的长野。
日常负责程序的运营。从今天开始，将分几次针对最近在Web应用的可扩展性领域
的热门话题memcached，与我公司开发部研究开发组的前坂一起，
说明其内部结构和使用。</p>
<h2 id="content_2_0">memcached是什么？</h2>
<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>
<div style="text-align: left;"><img src="http://pic001.cnblogs.com/img/dudu/200809/2008092816494460.png" alt="" /><br />
</div>
<p>图1 一般情况下memcached的用途</p>
<h2 id="content_2_1">memcached的特征</h2>
<p>memcached作为高速运行的分布式缓存服务器，具有以下的特点。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li>协议简单</li>
    <li>基于libevent的事件处理</li>
    <li>内置内存存储方式</li>
    <li>memcached不互相通信的分布式</li>
</ul>
<h3 id="content_2_2">协议简单</h3>
<p>memcached的服务器客户端通信并不使用复杂的XML等格式，
而使用简单的基于文本行的协议。因此，通过telnet
也能在memcached上保存数据、取得数据。下面是例子。</p>
<pre>$ telnet localhost 11211<br />
<br />
Trying 127.0.0.1...<br />
<br />
Connected to localhost.localdomain (127.0.0.1).<br />
<br />
Escape character is '^]'.<br />
<br />
set foo 0 0 3     （保存命令）<br />
<br />
bar               （数据）<br />
<br />
STORED            （结果）<br />
<br />
get foo           （取得命令）<br />
<br />
VALUE foo 0 3     （数据）<br />
<br />
bar               （数据）</pre>
<p>协议文档位于memcached的源代码内，也可以参考以下的URL。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <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>
<h3 id="content_2_3">基于libevent的事件处理</h3>
<p>libevent是个程序库，它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能
封装成统一的接口。即使对服务器的连接数增加，也能发挥O(1)的性能。
memcached使用这个libevent库，因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。
关于事件处理这里就不再详细介绍，可以参考Dan Kegel的The C10K Problem。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li><strong>libevent</strong>: <a href="http://www.monkey.org/%7Eprovos/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>
<h3 id="content_2_4">内置内存存储方式</h3>
<p>为了提高性能，memcached中保存的数据都存储在memcached内置的内存存储空间中。
由于数据仅存在于内存中，因此重启memcached、重启操作系统会导致全部数据消失。
另外，内容容量达到指定值之后，就基于LRU(Least Recently Used)算法自动删除不使用的缓存。
memcached本身是为缓存而设计的服务器，因此并没有过多考虑数据的永久性问题。
关于内存存储的详细信息，本连载的第二讲以后前坂会进行介绍，请届时参考。</p>
<h3 id="content_2_5">memcached不互相通信的分布式</h3>
<p>memcached尽管是&#8220;分布式&#8221;缓存服务器，但服务器端并没有分布式功能。
各个memcached不会互相通信以共享信息。那么，怎样进行分布式呢？
这完全取决于客户端的实现。本连载也将介绍memcached的分布式。</p>
<div style="text-align: left;"><img src="http://pic001.cnblogs.com/img/dudu/200809/2008092816503963.png" alt="" /><br />
</div>
<p>图2 memcached的分布式</p>
<p>接下来简单介绍一下memcached的使用方法。</p>
<h2 id="content_2_6">安装memcached</h2>
<p>memcached的安装比较简单，这里稍加说明。</p>
<p>memcached支持许多平台。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li>Linux</li>
    <li>FreeBSD</li>
    <li>Solaris (memcached 1.2.5以上版本)</li>
    <li>Mac OS X</li>
</ul>
<p>另外也能安装在Windows上。这里使用Fedora Core 8进行说明。</p>
<h3 id="content_2_7">memcached的安装</h3>
<p>运行memcached需要本文开头介绍的libevent库。Fedora 8中有现成的rpm包，
通过yum命令安装即可。</p>
<pre>$ sudo yum install libevent libevent-devel</pre>
<p>memcached的源代码可以从memcached网站上下载。本文执笔时的最新版本为1.2.5。
Fedora 8虽然也包含了memcached的rpm，但版本比较老。因为源代码安装并不困难，
这里就不使用rpm了。</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <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>$ wget http://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz<br />
<br />
$ tar zxf memcached-1.2.5.tar.gz<br />
<br />
$ cd memcached-1.2.5<br />
<br />
$ ./configure<br />
<br />
$ make<br />
<br />
$ sudo make install</pre>
<p>默认情况下memcached安装到/usr/local/bin下。</p>
<h3 id="content_2_8">memcached的启动</h3>
<p>从终端输入以下命令，启动memcached。</p>
<pre>$ /usr/local/bin/memcached -p 11211 -m 64m -vv<br />
<br />
slab class   1: chunk size     88 perslab 11915<br />
<br />
slab class   2: chunk size    112 perslab  9362<br />
<br />
slab class   3: chunk size    144 perslab  7281<br />
<br />
中间省略<br />
<br />
slab class  38: chunk size 391224 perslab     2<br />
<br />
slab class  39: chunk size 489032 perslab     2<br />
<br />
&lt;23 server listening<br />
<br />
&lt;24 send buffer was 110592, now 268435456<br />
<br />
&lt;24 server listening (udp)<br />
<br />
&lt;24 server listening (udp)<br />
<br />
&lt;24 server listening (udp)<br />
<br />
&lt;24 server listening (udp)</pre>
<p>这里显示了调试信息。这样就在前台启动了memcached，监听TCP端口11211
最大内存使用量为64M。调试信息的内容大部分是关于存储的信息，
下次连载时具体说明。</p>
<p>作为daemon后台启动时，只需</p>
<pre>$ /usr/local/bin/memcached -p 11211 -m 64m -d</pre>
<p>这里使用的memcached启动选项的内容如下。</p>
<div>
<table border="0" cellspacing="1">
    <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 vrebose模式启动，调试信息和错误输出到控制台</td>
        </tr>
        <tr>
            <td>-d</td>
            <td>作为daemon在后台启动</td>
        </tr>
    </tbody>
</table>
</div>
<p>上面四个是常用的启动选项，其他还有很多，通过</p>
<pre>$ /usr/local/bin/memcached -h</pre>
<p>命令可以显示。许多选项可以改变memcached的各种行为，
推荐读一读。</p>
<h2 id="content_2_9">用客户端连接</h2>
<p>许多语言都实现了连接memcached的客户端，其中以Perl、PHP为主。
仅仅memcached网站上列出的语言就有</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <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 style="padding-left: 16px; margin-left: 16px;">
    <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>
<h2 id="content_2_10">使用Cache::Memcached</h2>
<p>Perl的memcached客户端有</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <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 style="padding-left: 16px; margin-left: 16px;">
    <li><strong>Cache::Memcached - search.cpan.org</strong>: <a href="http://search.cpan.org/dist/Cache-Memcached/">http://search.cpan.org/dist/Cache-Memcached/</a></li>
</ul>
<h3 id="content_2_11">使用Cache::Memcached连接memcached</h3>
<p>下面的源代码为通过Cache::Memcached连接刚才启动的memcached的例子。</p>
<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">#</span><span style="color: #000000;">!/</span><span style="color: #000000;">usr</span><span style="color: #000000;">/</span><span style="color: #000000;">bin</span><span style="color: #000000;">/</span><span style="color: #000000;">perl<br />
<br />
<br />
<br />
use&nbsp;strict;<br />
<br />
use&nbsp;warnings;<br />
<br />
use&nbsp;Cache::Memcached;<br />
<br />
<br />
<br />
my&nbsp;$key&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">foo</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
<br />
my&nbsp;$value&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">bar</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br />
<br />
my&nbsp;$expires&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">3600</span><span style="color: #000000;">;&nbsp;#&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">&nbsp;hour<br />
<br />
my&nbsp;$memcached&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Cache::Memcached</span><span style="color: #000000;">-&gt;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">({<br />
<br />
servers&nbsp;</span><span style="color: #000000;">=&gt;</span><span style="color: #000000;">&nbsp;[</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 />
<br />
compress_threshold&nbsp;</span><span style="color: #000000;">=&gt;</span><span style="color: #000000;">&nbsp;10_000<br />
<br />
});<br />
<br />
<br />
<br />
$memcached</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">add($key,&nbsp;$value,&nbsp;$expires);<br />
<br />
my&nbsp;$ret&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;$memcached</span><span style="color: #000000;">-&gt;</span><span style="color: #0000ff;">get</span><span style="color: #000000;">($key);<br />
<br />
print&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">$ret</span><span style="color: #000000;">"</span><span style="color: #000000;">n</span><span style="color: #000000;">"</span><span style="color: #000000;">;</span></div>
<p>在这里，为Cache::Memcached指定了memcached服务器的IP地址和一个选项，以生成实例。
Cache::Memcached常用的选项如下所示。</p>
<div>
<table border="0" cellspacing="1">
    <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>
</div>
<p>另外，Cache::Memcached通过Storable模块可以将Perl的复杂数据序列化之后再保存，
因此散列、数组、对象等都可以直接保存到memcached中。</p>
<h3 id="content_2_12">保存数据</h3>
<p>向memcached保存数据的方法有</p>
<ul style="padding-left: 16px; margin-left: 16px;">
    <li>add</li>
    <li>replace</li>
    <li>set</li>
</ul>
<p>它们的使用方法都相同：</p>
<pre>my $add = $memcached-&gt;add( '键', '值', '期限' );<br />
<br />
my $replace = $memcached-&gt;replace( '键', '值', '期限' );<br />
<br />
my $set = $memcached-&gt;set( '键', '值', '期限' );</pre>
<p>向memcached保存数据时可以指定期限(秒)。不指定期限时，memcached按照LRU算法保存数据。
这三个方法的区别如下：</p>
<div>
<table border="0" cellspacing="1">
    <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>
</div>
<h3 id="content_2_13">获取数据</h3>
<p>获取数据可以使用get和get_multi方法。</p>
<pre>my $val = $memcached-&gt;get('键');<br />
<br />
my $val = $memcached-&gt;get_multi('键1', '键2', '键3', '键4', '键5');</pre>
<p>一次取得多条数据时使用get_multi。get_multi可以非同步地同时取得多个键值，
其速度要比循环调用get快数十倍。</p>
<h3 id="content_2_14">删除数据</h3>
<p>删除数据使用delete方法，不过它有个独特的功能。</p>
<pre>$memcached-&gt;delete('键', '阻塞时间(秒)');</pre>
<p>删除第一个参数指定的键的数据。第二个参数指定一个时间值，可以禁止使用同样的键保存新数据。
此功能可以用于防止缓存数据的不完整。但是要注意，<strong>set函数忽视该阻塞，照常保存数据</strong></p>
<h3 id="content_2_15">增一和减一操作</h3>
<p>可以将memcached上特定的键值作为计数器使用。</p>
<pre>my $ret = $memcached-&gt;incr('键');<br />
<br />
$memcached-&gt;add('键', 0) unless defined $ret;</pre>
<p>增一和减一是原子操作，但未设置初始值时，不会自动赋成0。因此，
应当进行错误检查，必要时加入初始化操作。而且，服务器端也不会对
超过2&lt;sup&gt;32&lt;/sup&gt;时的行为进行检查。</p>
<h2 id="content_2_16">总结</h2>
<p>这次简单介绍了memcached，以及它的安装方法、Perl客户端Cache::Memcached的用法。
只要知道，memcached的使用方法十分简单就足够了。</p>
<p>下次由前坂来说明memcached的内部结构。了解memcached的内部构造，
就能知道如何使用memcached才能使Web应用的速度更上一层楼。
欢迎继续阅读下一章。</p>
</div>
<img src ="http://www.blogjava.net/sway/aggbug/234399.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 11:28 <a href="http://www.blogjava.net/sway/articles/234399.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Memcached深度分析</title><link>http://www.blogjava.net/sway/articles/234360.html</link><dc:creator>sway</dc:creator><author>sway</author><pubDate>Wed, 15 Oct 2008 01:31:00 GMT</pubDate><guid>http://www.blogjava.net/sway/articles/234360.html</guid><wfw:comment>http://www.blogjava.net/sway/comments/234360.html</wfw:comment><comments>http://www.blogjava.net/sway/articles/234360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sway/comments/commentRss/234360.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sway/services/trackbacks/234360.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Memcached深度分析作者：奶瓶网站：http://www.54np.comMemcached是danga.com（运营LiveJournal的技术团队）开发的一套分布式内存对象缓存系统，用于在动态系统中减少数据库负载，提升性能。关于这个东西，相信很多人都用过，本文意在通过对memcached的实现及代码分析，获得对这个出色的开源软件更深入的了解，并可以根据我们的需要对其进...&nbsp;&nbsp;<a href='http://www.blogjava.net/sway/articles/234360.html'>阅读全文</a><img src ="http://www.blogjava.net/sway/aggbug/234360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sway/" target="_blank">sway</a> 2008-10-15 09:31 <a href="http://www.blogjava.net/sway/articles/234360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>