转载地址:http://sameveryday.blog.163.com/blog/static/178072337201411095357740/ java
Memcache工做原理
linux
首 先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户真个链接操纵,客户端能够由各类语言编写,目前已知的客户端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。客户端在与 memcached 服务创建链接以后,接下来的事情就是存取对象了,每一个被存取的对象都有一个惟一的标识符 key,存取操纵均经过这个 key 进行,保存到 memcached 中的对象其实是放置内存中的,并非保存在 cache 文件中的,这也是为何 memcached 可以如此高效快速的缘由。留意,这些对象并非持久的,服务中止以后,里边的数据就会丢失。
与 不少 cache 工具相似,Memcached 的原理并不复杂。它采用了C/S的模式,在 server 端启动服务进程,在启动时能够指定监听的 ip,本身的端口号,所使用的内存大小等几个关键参数。一旦启动,服务就一直处于可用状态。Memcached 的目前版本是经过C实现,采用了单进程,单线程,异步I/O,基于事件 (event_based) 的服务方式.使用 libevent 做为事件通知实现。多个 Server 能够协同工做,但这些 Server 之间是没有任何通信联系的,每一个 Server 只是对本身的数据进行治理。Client 端经过指定 Server 真个 ip 地址(经过域名应该也能够)。须要缓存的对象或数据是以 key->value 对的形式保存在Server端。key 的值经过 hash 进行转换,根据 hash 值把 value 传递到对应的具体的某个 Server 上。当须要获取对象数据时,也根据 key 进行。首先对 key 进行 hash,经过得到的值能够肯定它被保存在了哪台 Server 上,而后再向该 Server 发出请求。Client 端只须要知道保存 hash(key) 的值在哪台服务器上就能够了。
c++
实在说到底,memcache 的工做就是在专门的机器的内存里维护一张巨大的 hash 表,来存储常常被读写的一些数组与文件,从而极大的进步网站的运行效率。数组
memcache 命中率缓存
首先查看,服务器
在linux或window上有telnet链接memcache,而后输入stats会出现下面的命令,这些状态的说明以下:异步
pid
memcache服务器的进程IDide
uptime
服务器已经运行的秒数memcached
time
服务器当前的unix时间戳工具
version
memcache版本
pointer_size
当前操做系统的指针大小(32位系统通常是32bit)
rusage_user
进程的累计用户时间
rusage_system
进程的累计系统时间
curr_items
服务器当前存储的items数量
total_items
从服务器启动之后存储的items总数量
bytes
当前服务器存储items占用的字节数
curr_connections
当前打开着的链接数
total_connections
从服务器启动之后曾经打开过的链接数
connection_structures
服务器分配的链接构造数
cmd_get
get命令(获取)总请求次数
cmd_set
set命令(保存)总请求次数
get_hits
总命中次数
get_misses
总未命中次数
evictions
为获取空闲内存而删除的items数(分配给memcache的空间用满后须要删除旧的items来获得空间分配给新的items)
bytes_read
总读取字节数(请求字节数)
bytes_written
总发送字节数(结果字节数)
limit_maxbytes
分配给memcache的内存大小(字节)
threads
当前线程数
1、缓存命中率 = get_hits/cmd_get * 100%
2、get_misses的数字加上get_hits应该等于cmd_get
3、total_items == cmd_set == get_misses,当可用最大内存用光时,memcached就会删掉一些内容,等式就会不成立
memcached/scripts/memcached-too
Memcached, 人所皆知的remote distribute cache(不知道的能够javaeye一下下,或者google一下下,或者baidu一下下,可是鉴于baidu的排名商业味道太浓(从最近得某某事 件能够看出),因此仍是建议javaeye一下下),使用起来也很是的简单,它被用在了不少网站上面,几乎不多有大型的网站不会使用 memcached。
曾经我也看过不少剖析memcached内部机制的文章,有一点收获,可是看过以后又忘记了,并且没有什么深入的概念,可是最近我遇到一个问题,这个问题迫使我从新来认识memcache,下面我阐述一下我遇到的问题
问题:我有几千万的数据,这些数据会常常被用到,目前来看,它必需要放到memcached中,以保证访问速度,可是个人memcached中数据常常会 有丢失,而业务需求是memcached中的数据是不能丢失的。个人数据丢失的时候,memcached server的内存才使用到60%,也就是还有40%内存被严重的浪费掉了。但不是全部的应用都是这样,其余应用内存浪费的就比较少。为何内存才使用到 60%的时候LRU就执行了呢(之因此肯定是LRU执行是由于我发现个人数据丢失的老是前面放进去的,并且这个过程当中,这些数据都没有被访问,好比第一次 访问的时候,只能访问第1000w条,而第300w条或者以前的数据都已经丢失了,从日志里看,第300w条确定是放进去了)。
带着这些疑问,我开始从新审视memcached这个产品,首先从它的内存模型开始:咱们知道c++里分配内存有两种方式,预先分配和动态分配,显然,预 先分配内存会使程序比较快,可是它的缺点是不能有效利用内存,而动态分配能够有效利用内存,可是会使程序运行效率降低,memcached的内存分配就是 基于以上原理,显然为了得到更快的速度,有时候咱们不得不以空间换时间。
也就是说memcached会预先分配内存,对了,memcached分配内存方式称之为allocator,首先,这里有3个概念:
1 slab
2 page
3 chunk
解释一下,通常来讲一个memcahced进程会预先将本身划分为若干个slab,每一个slab下又有若干个page,每一个page下又有多个 chunk,若是咱们把这3个咚咚看做是object得话,这是两个一对多得关系。再通常来讲,slab得数量是有限得,几个,十几个,或者几十个,这个 跟进程配置得内存有关。而每一个slab下得page默认状况是1m,也就是说若是一个slab占用100m得内存得话,那么默认状况下这个slab所拥有 得page得个数就是100,而chunk就是咱们得数据存放得最终地方。
举一个例子,我启动一个memcached进程,占用内存100m,再打开telnet,telnet localhost 11211,链接上memcache以后,输入stats slabs,回车,出现以下数据:
STAT 1:chunk_size 80
STAT 1:chunks_per_page 13107
STAT 1:total_pages 1
STAT 1:total_chunks 13107
STAT 1:used_chunks 13107
STAT 1:free_chunks 0
STAT 1:free_chunks_end 13107
STAT 2:chunk_size 100
STAT 2:chunks_per_page 10485
STAT 2:total_pages 1
STAT 2:total_chunks 10485
STAT 2:used_chunks 10485
STAT 2:free_chunks 0
STAT 2:free_chunks_end 10485
STAT 3:chunk_size 128
STAT 3:chunks_per_page 8192
STAT 3:total_pages 1
STAT 3:total_chunks 8192
STAT 3:used_chunks 8192
STAT 3:free_chunks 0
STAT 3:free_chunks_end 8192
以上就是前3个slab得详细信息
chunk_size表示数据存放块得大小,chunks_per_page表示一个内存页page中拥有得chunk得数量,total_pages表 示每一个slab下page得个数。total_chunks表示这个slab下chunk得总数(=total_pages * chunks_per_page),used_chunks表示该slab下已经使用得chunk得数量,free_chunks表示该slab下还能够 使用得chunks数量。
从上面得示例slab 1一共有1m得内存空间,并且如今已经被用完了,slab2也有1m得内存空间,也被用完了,slab3得状况依然如此。 并且从这3个slab中chunk得size能够看出来,第一个chunk为80b,第二个是100b,第3个是128b,基本上后一个是前一个得 1.25倍,可是这个增加状况咱们是能够控制得,咱们能够经过在启动时得进程参数 –f来修改这个值,好比说 –f 1.1表示这个增加因子为1.1,那么第一个slab中得chunk为80b得话,第二个slab中得chunk应该是80*1.1左右。
解释了这么多也该能够看出来我遇到得问题得缘由了,若是还看不出来,那我再补充关键的一句:memcached中新的value过来存放的地址是该 value的大小决定的,value老是会被选择存放到chunk与其最接近的一个slab中,好比上面的例子,若是个人value是80b,那么我这所 有的value老是会被存放到1号slab中,而1号slab中的free_chunks已是0了,怎么办呢,若是你在启动memcached的时候没 有追加-M(禁止LRU,这种状况下内存不够时会out of memory),那么memcached会把这个slab中最近最少被使用的chunk中的数据清掉,而后放上最新的数据。这就解释了为何个人内存还有 40%的时候LRU就执行了,由于个人其余slab中的chunk_size都远大于个人value,因此个人value根本不会放到那几个slab中, 而只会放到和个人value最接近的chunk所在的slab中(而这些slab早就满了,郁闷了)。这就致使了个人数据被不停的覆盖,后者覆盖前者。
问题找到了,解决方案仍是没有找到,由于个人数据必需要求命中率时100%,我只能经过调整slab的增加因子和page的大小来尽可能来使命中率接近 100%,可是并不能100%保证命中率是100%(这话怎么读起来这么别扭呢,自我反省一下本身的语文水平),若是您说,这种方案不行啊,由于个人 memcached server不能停啊,没关系还有另一个方法,就是memcached-tool,执行move命令,如:move 3 1,表明把3号slab中的一个内存页移动到1号slab中,有人问了,这有什么用呢,好比说个人20号slab的利用率很是低,可是page却又不少, 好比200,那么就是200m,而2好slab常常发生LRU,明显page不够,我就能够move 20 2,把20号slab的一个内存页移动到2号slab上,这样就能更加有效的利用内存了(有人说了,一次只移动一个page,多麻烦啊?ahuaxuan 说,仍是写个脚本,循环一下吧)。
有人说不行啊,个人memcache中的数据不能丢失啊,ok,试试新浪的memcachedb吧,虽然我没有用过,可是建议你们能够试试,它也使利用 memcache协议和berkeleyDB作的(写到这里,我不得不佩服danga了,我以为它最大的贡献不是memcache server自己,而是memcache协议),听说它被用在新浪的很多应用上,包括新浪的博客。
补充,stats slab命令能够查看memcached中slab的状况,而stats命令能够查看你的memcached的一些健康状况,好比说命中率之类的,示例以下:
STAT pid 2232
STAT uptime 1348
STAT time 1218120955
STAT version 1.2.1
STAT pointer_size 32
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 1
STAT total_connections 3
STAT connection_structures 2
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT bytes_read 26
STAT bytes_written 16655
STAT limit_maxbytes 104857600
从上面的数据能够看到这个memcached进程的命中率很好,get_misses低达0个,怎么回事啊,由于这个进程使我刚启动的,我只用 telnet连了一下,因此curr_connections为1,而total_items为0,由于我没有放数据进去,get_hits为0,由于我 没有调用get方法,最后的结果就是misses固然为0,哇哦,换句话说命中率就是100%,又yy了。 该到总结的时候了,从这篇文章里咱们能够获得如下几个结论: 结论一,memcached得LRU不是全局的,而是针对slab的,能够说是区域性的。 结论二,要提升memcached的命中率,预估咱们的value大小而且适当的调整内存页大小和增加因子是必须的。 结论三,带着问题找答案理解的要比随便看看的效果好得多。