Memcache 详细介绍

jopen 12年前
     <div>     Memcache 详细介绍    </div>    <pre class="brush:cpp; toolbar: true; auto-links: false;">#include “stdio.h” #include “stdlib.h” #include “string.h” #include “libmemcached/memcached.h” //gcc -o cc cc.c  -L /usr/local/lib -lmemcached int main(int argc, char *argv[]) { memcached_st *memc; memcached_return rc; memcached_server_st *servers; char value[8191]; //connect multi server memc = memcached_create(NULL); servers = memcached_server_list_append(NULL, “localhost”, 11211, &rc); //servers = memcached_server_list_append(servers, “localhost”, 11212, &rc); rc = memcached_server_push(memc, servers); memcached_server_free(servers); //Save multi data size_t i; char *keys[]= {“key1″, “key2″, “key3″}; size_t key_length[]= {4, 4, 4}; char *values[] = {“This is c first value”, “This is c second value”, “This is c third value”}; size_t val_length[]= {21, 22, 21}; for (i=0; i <3; i++) { rc = memcached_set(memc, keys[i], key_length[i], values[i], val_length[i], (time_t)180, (uint32_t)0); if (rc == MEMCACHED_SUCCESS) { printf(“Save key:%s data:”%s” success.n”,keys[i], values[i]); } }  char return_key[MEMCACHED_MAX_KEY]; size_t return_key_length; char *return_value; size_t return_value_length; uint32_t flags; rc = memcached_mget(memc, keys, key_length, 3); while ((return_value = memcached_fetch(memc, return_key,&return_key_length, &return_value_length, &flags, &rc))) { if (rc == MEMCACHED_SUCCESS) { printf(“Fetch key:%s data:%sn”, return_key, return_value); } } //Delete multi data for (i=0; i <3; i++) { rc = memcached_set(memc, keys[i], key_length[i], values[i], val_length[i], (time_t)180, (uint32_t)0); rc = memcached_delete(memc, keys[i], key_length[i], (time_t)0); if (rc == MEMCACHED_SUCCESS) { printf(“Delete %s successn”, keys[i], values[i]); } } //free memcached_free(memc); return 0; }</pre>    <br />    <div>     <div>      <div>       Memcached一些特性和限制       <br /> •在 Memcached中可以保存的item数据量是没有限制的,只有内存足够       <br /> • Memcached单进程最大使用内存为2G,要使用更多内存,可以分多个端口开启多个Memcached进程       <br /> •最大30天的数据过期时间,设置为永久的也会在这个时间过期,常量REALTIME_MAXDELTA       <br /> 60*60*24*30控制       <br /> •最大键长为250字节,大于该长度无法存储,常量KEY_MAX_LENGTH 250控制       <br /> •单个item最大数据是1MB,超过1MB数据不予存储,常量POWER_BLOCK 1048576进行控制,       <br /> 它是默认的slab大小       <br /> •最大同时连接数是200,通过 conn_init()中的freetotal进行控制,最大软连接数是1024,通过       <br /> settings.maxconns=1024 进行控制       <br /> •跟空间占用相关的参数:settings.factor=1.25, settings.chunk_size=48, 影响slab的数据占用和步进方式       <p>memcached是一种无阻塞的socket通信方式服务,基于libevent库,由于无阻塞通信,对内存读写速度非常之快。<br /> memcached分服务器端和客户端,可以配置多个服务器端和客户端,应用于分布式的服务非常广泛。<br /> memcached作为小规模的数据分布式平台是十分有效果的。</p>      </div>      <div id="more">       memcached是键值一一对应,key默认最大不能超过128个字 节,value默认大小是1M,也就是一个slabs,如果要存2M的值(连续的),不能用两个slabs,因为两个slabs不是连续的,无法在内存中 存储,故需要修改slabs的大小,多个key和value进行存储时,即使这个slabs没有利用完,那么也不会存放别的数据。       <p>目前memcached支持C/C++、Perl、PHP、Python、Ruby、Java、C#、Postgres、Chicken Scheme、Lua、MySQL和Protocol等语言客户端。</p>       <p>memcached本身的服务是用C语言编写的,启动非常简单:</p>       <blockquote>        <pre>memcached -d -m 2048 -l 10.0.0.40 -p 11211</pre>       </blockquote>       <pre>-d表示memcached启动作为一个守护进程; -m表示启用多大的内存支持,这里的单位是兆(M),2048表示2个G的内存 -l表示服务的IP地址,最好不要写成localhost -p表示服务的端口,默认为11211</pre>      </div>      <p>协议</p>      <p>结束会话不需要发送任何命令。当不再需memcached服务时,要客户端可以在任何时候关闭连接。需要注意的是,鼓励客户端缓存这些连接,而不是 每次需要存取数据时都重新打开连接。这是因为memcached 被特意设计成及时开启很多连接也能够高效的工作(数百个,上千个如果需要的话)。缓存这些连接,可以消除建立连接所带来的开销(/*/相对而言,在服务器 端建立一个新连接的准备工作所带来的开销,可以忽略不计。)。</p>      <p>在memcache协议中发送的数据分两种:文本行 和 自由数据。文本行被用于来自客户端的命令和服务器的回应。自由数据用于客户端从服务器端存取数据时。同样服务器会以字节流的方式传回自由数据。/*/服务 器不用关心自由数据的字节顺序。自由数据的特征没有任何限制;但是通过前文提到的文本行,这项数据的接受者(服务器或客户端),便能够精确地获知所发送的 数据库的长度。</p>      <p>文本行固定以“rn”(回车符紧跟一个换行符)结束。 自由数据也是同样会以“rn”结束,但是 r(回车符)、n(换行符),以及任何其他8位字符,均可出现在数据中。因此,当客户端从服务器取回数据时,必须使用数据区块的长度来确定数据区块的结束 位置,而不要依据数据区块末尾的“rn”,即使它们固定存在于此。</p>      <p><strong>键值</strong><br /> 存储在memcached中的数据通过键值来标识。键值是一个文本字符串,对于需要存取这项数据的客户端而言,它必须是唯一的。键值当前的长度限制设定为250字符(当然,客户端通常不会用到这么长的键);键值中不能使用制表符和其他空白字符(例如空格,换行等)。</p>      <p><strong>命令</strong><br /> 所有命令分为3种类型:<br /> 存储命令(有3项:’set’、’add’、’repalce’)指示服务器储存一些由键值标识的数据。客户端发送一行命令,后面跟着数据区块;然后,客户端等待接收服务器回传的命令行,指示成功与否。<br /> 取回命令(只有一项:’get’)指示服务器返回与所给键值相符合的数据(一个请求中右一个或多个键值)。客户端发送一行命令,包括所有请求的键值;服务 器每找到一项内容,都会发送回客户端一行关于这项内容的信息,紧跟着是对应的数据区块;直到服务器以一行“END”回应命令结束。<br /> /*?*/其他的命令都不能携带自由数据。在这些命令中,客户端发送一行命令,然后等待(由命令所决定)一行回应,或最终以一行“END”结束的多行命令。</p>      <p>一行命令固定以命令名称开始,接着是以空格隔开的参数(如果有参数的话)。命令名称大小写敏感,并且必须小写。一些客户端发送给服务器的命令会包含 一些时限(针对内容或客户端请求的操作)。这时,时限的具体内容既可以是Unix时间戳(从1970年1月1日开始的秒钟数),或当前时间开始的秒钟数。 对后者而言,不能超过 60*60*24*30(30天);如果超出,服务器将会理解为Unix时间戳,而不是从当前时间起的秒偏移。</p>      <p><strong>错误字串</strong><br /> 每一个由客户端发送的命令,都可能收到来自服务器的错误字串回复。这些错误字串会以三种形式出现:<br /> - “ERRORrn”<br /> 意味着客户端发送了不存在的命令名称。<br /> - “CLIENT_ERROR rn”<br /> 意味着输入的命令行里存在一些客户端错误,例如输入未遵循协议。部分是人类易于理解的错误解说……<br /> - “SERVER_ERROR rn”<br /> 意味着一些服务器错误,导致命令无法执行。部分是人类易于理解的错误解说。在一些严重的情形下(通常应该不会遇到),服务器将在发送这行错误后关闭连接。这是服务器主动关闭连接的唯一情况。<br /> 在后面每项命令的描述中,这些错误行不会再特别提到,但是客户端必须考虑到这些它们存在的可能性。</p>      <p><strong>存储命令</strong></p>      <table class="ke-zeroborder" border="0" cellspacing="1" cellpadding="4" width="100%">       <tbody>        <tr>         <td bgcolor="#cccccc" valign="top" colspan="2"><command name> <key> <flags> <exptime> <bytes>rn</td>        </tr>        <tr>         <td valign="top">- <command name> 是 set, add, 或者 repalce</td>        </tr>       </tbody>      </table>      <ul>       <li>set 意思是 “储存此数据”</li>       <li>add 意思是 “储存此数据,只在服务器*未*保留此键值的数据时”</li>       <li>replace意思是 “储存此数据,只在服务器*曾*保留此键值的数据时”</li>      </ul>      <table class="ke-zeroborder" border="0" cellspacing="1" cellpadding="4" width="100%">       <tbody>        <tr>         <td valign="top">- <key> 是接下来的客户端所要求储存的数据的键值</td>         <td valign="top"><br /> </td>        </tr>        <tr>         <td valign="top">- <flags> 是在取回内容时,与数据和发送块一同保存服务器上的任意16位无符号整形(用十进制来书写)。客户端可以用它作为“位域”来存储一些特定的信息;它对服务器是不透明的。</td>        </tr>       </tbody>      </table>      <p>- <exptime> 是终止时间。如果为0,该项永不过期(虽然它可能被删除,以便为其他缓存项目腾出位置)。如果非0(Unix时间戳或当前时刻的秒偏移),到达终止时间后,客户端无法再获得这项内容。<br /> - <bytes> 是随后的数据区块的字节长度,不包括用于分野的“rn”。它可以是0(这时后面跟随一个空的数据区块)。</p>      <p>发送命令行和数据区块以后,客户端等待回复,可能的回复如下:<br /> - “STOREDrn”<br /> 表明成功.<br /> - “NOT_STOREDrn”<br /> 表明数据没有被存储,但不是因为发生错误。这通常意味着add 或 replace命令的条件不成立,或者,项目已经位列删除队列(参考后文的“delete”命令)。</p>      <p><strong>取回命令</strong></p>      <table class="ke-zeroborder" border="0" cellspacing="1" cellpadding="4" width="100%">       <tbody>        <tr>         <td bgcolor="#cccccc" valign="top" colspan="2">get <key>*rn</td>        </tr>        <tr>         <td valign="top">- <key>* 表示一个或多个键值,由空格隔开的字串</td>        </tr>       </tbody>      </table>      <p>这行命令以后,客户端的等待0个或多个项目,每项都会收到一行文本,然后跟着数据区块。所有项目传送完毕后,服务器发送以下字串:<br /> “ENDrn”<br /> 来指示回应完毕。<br /> 服务器用以下形式发送每项内容:<br /> VALUE rn<br /> rn<br /> - 是所发送的键名<br /> - 是存储命令所设置的记号<br /> - 是随后数据块的长度,*不包括* 它的界定符“rn”<br /> - 是发送的数据如果在取回请求中发送了一些键名,而服务器没有送回项目列表,这意味着服务器没这些键名(可能因为它们从未被存储,或者为给其他内容腾出空间而被删除,或者到期,或者被已客户端删除)。</p>      <p><strong>删除</strong><br /> 命令“delete”允许从外部删除内容:<br /> delete rn<br /> - 是客户端希望服务器删除的内容的键名<br /> - 是一个单位为秒的时间(或代表直到某一刻的Unix时间),在该时间内服务器会拒绝对于此键名的“add”和 “replace”命令。此时内容被放入 delete队列,无法再通过“get”得到该内容,也无法是用“add”和“replace”命令(但是“set”命令可用)。直到指定时间,这些内容 被最终从服务器的内存中彻底清除。<br /> 参数 是可选的,缺省为0(表示内容会立刻清除,并且随后的存储命令均可用)。<br /> 此命令有一行回应:<br /> - “DELETEDrn”<br /> 表示执行成功<br /> - “NOT_FOUNDrn”<br /> 表示没有找到这项内容</p>      <p>参考随后的“flush_all”命令使所有内容无效</p>      <p><strong>增加/减少</strong><br /> 命令 “incr” 和 “decr”被用来修改数据,当一些内容需要 替换、增加或减少时。这些数据必须是十进制的32位无符号整新。如果不是,则当作0来处理。修改的内容必须存在,当使用“incr”/“decr”命令修 改不存在的内容时,不会被当作0处理,而是操作失败。</p>      <p>客户端发送命令行:<br /> incr rn<br /> 或<br /> decr rn<br /> - 是客户端希望修改的内容的建名<br /> - 是客户端要增加/减少的总数。</p>      <p>回复为以下集中情形:<br /> - “NOT_FOUNDrn”<br /> 指示该项内容的值,不存在。<br /> - rn ,是 增加/减少 。<br /> 注意”decr”命令发生下溢:如果客户端尝试减少的结果小于0时,结果会是0。”incr” 命令不会发生溢出。</p>      <p><strong>状态</strong><br /> 命令”stats” 被用于查询服务器的运行状态和其他内部数据。有两种格式。不带参数的:<br /> statsrn<br /> 这会在随后输出各项状态、设定值和文档。另一种格式带有一些参数:<br /> stats rn<br /> 通过,服务器传回各种内部数据。因为随时可能发生变动,本文不提供参数的种类及其传回数据。</p>      <p><strong>各种状态</strong><br /> 受到无参数的”stats”命令后,服务器发送多行内容,如下:<br /> STAT rn<br /> 服务器用以下一行来终止这个清单:<br /> ENDrn<br /> 在每行状态中, 是状态的名字, 使状态的数据。 以下清单,是所有的状态名称,数据类型,和数据代表的含义。<br /> 在“类型”一列中,”32u”表示32位无符号整型,”64u”表示64位无符号整型,”32u:32u”表示用冒号隔开的两个32位无符号整型。</p>      <table class="ke-zeroborder" border="0" cellspacing="1" cellpadding="2" width="94%" bgcolor="#003366">       <tbody>        <tr>         <td bgcolor="#cc9933" width="100" align="middle"><strong>名称</strong></td>         <td bgcolor="#cc9933" width="80" align="middle"><strong>类型</strong></td>         <td bgcolor="#cc9933" colspan="2" align="middle"><strong>含义</strong></td>        </tr>        <tr>         <td bgcolor="#ffe3aa">pid</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器进程ID</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">uptime</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器运行时间,单位秒</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">time</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器当前的UNIX时间</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">version</td>         <td bgcolor="#ffe3aa">string</td>         <td bgcolor="#ffe3aa">服务器的版本号</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">rusage_user</td>         <td bgcolor="#ffe3aa">32u:32u</td>         <td bgcolor="#ffe3aa">该进程累计的用户时间<br /> (秒:微妙)</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">rusage_system</td>         <td bgcolor="#ffe3aa">32u:32u</td>         <td bgcolor="#ffe3aa">该进程累计的系统时间<br /> (秒:微妙)</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">curr_items</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器当前存储的内容数量</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">total_items</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器启动以来存储过的内容总数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">bytes</td>         <td bgcolor="#ffe3aa">64u</td>         <td bgcolor="#ffe3aa">服务器当前存储内容所占用的字节数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">curr_connections</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">连接数量</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">total_connections</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器运行以来接受的连接总数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">connection_structures</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器分配的连接结构的数量</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">cmd_get</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">取回请求总数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">cmd_set</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">存储请求总数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">get_hits</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">请求成功的总次数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">get_misses</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">请求失败的总次数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">bytes_read</td>         <td bgcolor="#ffe3aa">64u</td>         <td bgcolor="#ffe3aa">服务器从网络读取到的总字节数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">bytes_written</td>         <td bgcolor="#ffe3aa">64u</td>         <td bgcolor="#ffe3aa">服务器向网络发送的总字节数</td>        </tr>        <tr>         <td bgcolor="#ffe3aa">limit_maxbytes</td>         <td bgcolor="#ffe3aa">32u</td>         <td bgcolor="#ffe3aa">服务器在存储时被允许使用的字节总数</td>        </tr>       </tbody>      </table>      <p><strong>其它命令</strong><br /> “flush_all”命令有一个可选的数字参数。它总是执行成功,服务器会发送“OKrn”回应。它的效果是使已经存在的项目立即失效(缺省),或在指 定的时间后。此后执行取回命令,将不会有任何内容返回(除非重新存储同样的键名)。flush_all 实际上没有立即释放项目所占用的内存,而是在随后陆续有新的项目被储存时执行。flush_all 效果具体如下:它导致所有更新时间早于flush_all所设定时间的项目,在被执行取回命令时命令被忽略。<br /> “version”命令没有参数:<br /> versionrn<br /> 在回应中,服务器发送:<br /> “VERSION rn”<br /> 是服务器的版本字串。<br /> “quit”命令没有参数:<br /> quitrn<br /> 接收此命令后,服务器关闭连接。不过,客户端可以在不再需要时,简单地关闭连接就行,并不一定需要发送这个命令。</p>      <p><strong>UDP 协议</strong><br /> 当来自客户端的连接数远大于TCP连接的上限时,可以使用基于UDP的接口。UDP接口不能保证传输到位,所以只有在不要求成功的操作中使用;比如被用于一个“get”请求时,会因不当的缓存处理而发生错误或回应有遗失。</p>      <p>每个UDP数据包都包含一个简单的帧头,数据之后的内容与TCP协议的描述类似。在执行所产生的数据流中,请求必须被包含在单独的一个UDP数据包中,但是回应可能跨越多个数据包。(只有“get”和“set”请求例外,跨越了多个数据包)</p>      <p>帧头有8字节长,如下(均由16位整数组成,网络字节顺序,高位在前):</p>      <ul>       <li>0-1 请求ID</li>       <li>2-3 序号</li>       <li>4-5 该信息的数据包总数</li>       <li>6-7 保留位,必须为0</li>      </ul>      <p>请求ID有客户端提供。一般它会是一个从随机基数开始的递增值,不过客户端想用什么样的请求ID都可以。服务器的回应会包含一个和请求中的同样的 ID。客户端使用请求ID来区分每一个回应。任何一个没有请求ID的数据包,可能是之前的请求遭到延迟而造成的,应该被丢弃。序号的返回是从0到n- 1,n是该条信息的数据包数量。<br /> memcached 的客户端使用TCP链接 与 服务器通讯。(UDP接口也同样有效,参考后文的 “UDP协议” )一个运行中的memcached服务器监视一些(可设置)端口。客户端连接这些端口,发送命令到服务器,读取回应,最后关闭连接。</p>     </div>    </div>