`
ahuaxuan
  • 浏览: 633247 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

memcached server LRU 深入分析

阅读更多
/**

*作者:张荣华

*日期:2008-08-08

**/

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大小并且适当的调整内存页大小和增长因子是必须的。
结论三,带着问题找答案理解的要比随便看看的效果好得多。

Ok,晚了,睡了。


注明:由于ahuaxuan水平有限,文中不妥之处还望不吝指正,谢谢。
分享到:
评论
25 楼 noblemoon 2013-03-04  
楼主,有个问题想问一下。还有40%的剩余内存。这时候mem为什么不能够在申请一个page,而必须要执行LRU来获得新的item的存储空间呢?
我的mem有的在80%左右、50%左右触发LRU,网上有些帖子在92%触发。按理说还有那么多内存空间可以分配的。谢谢。
24 楼 qzlake 2008-12-14  
ahuaxuan 写道
tongjian 写道
补充:
晕了,本来想编辑帖子,一不小心把上边帖子引用楼主的内容删了。还是再回一个吧
两点误解的补充:
第一点首先楼主说一个slab下有多个page,我理解page数指的是某一个slab的数量。
第二点我的理解是如果还有可分配的内存,则不会覆盖1号slab的数据,如果内存没有可分配的了,则会覆盖1号slab比较旧的数据。此时你说“我的内存还有40%的时候LRU就执行了”,其实此时已经没有内存可分配了,因为这40%内存已经分配给了其他号的slab,所以1号slab的旧数据会被LRU。


还有
“Keep in mind that memcached is a cache not a database. It is fast, but not reliable storage.”
不知大家怎么理解的?



第一点得问题,没啥好说得,看memcached得数据结构吧

第二点,我说我得"我的内存还有40%的时候LRU就执行了",这个是那个场景得现象,现象不代表本质.而且问题得本质我在正文中已经说明了.

看看我的回复,
ahuaxuan 写道

一个slab会有多个page,明白不?不可能只用1m(只是预先分配1m,不是说只能使用1m,当这 1m的数据满了之后,会重新分配一个page给这个slab,一旦到达使用内存的上限就不会再分配新的page了,新数据进来会有针对该slab的 LRU,而且这个LRU只是针对该双向链表的前50个元素),看清楚文章再发言可以吗

你们的memcached是放在什么系统上跑的?
23 楼 ahuaxuan 2008-12-05  
tongjian 写道
补充:
晕了,本来想编辑帖子,一不小心把上边帖子引用楼主的内容删了。还是再回一个吧
两点误解的补充:
第一点首先楼主说一个slab下有多个page,我理解page数指的是某一个slab的数量。
第二点我的理解是如果还有可分配的内存,则不会覆盖1号slab的数据,如果内存没有可分配的了,则会覆盖1号slab比较旧的数据。此时你说“我的内存还有40%的时候LRU就执行了”,其实此时已经没有内存可分配了,因为这40%内存已经分配给了其他号的slab,所以1号slab的旧数据会被LRU。


还有
“Keep in mind that memcached is a cache not a database. It is fast, but not reliable storage.”
不知大家怎么理解的?



第一点得问题,没啥好说得,看memcached得数据结构吧

第二点,我说我得"我的内存还有40%的时候LRU就执行了",这个是那个场景得现象,现象不代表本质.而且问题得本质我在正文中已经说明了.

看看我的回复,
ahuaxuan 写道

一个slab会有多个page,明白不?不可能只用1m(只是预先分配1m,不是说只能使用1m,当这 1m的数据满了之后,会重新分配一个page给这个slab,一旦到达使用内存的上限就不会再分配新的page了,新数据进来会有针对该slab的 LRU,而且这个LRU只是针对该双向链表的前50个元素),看清楚文章再发言可以吗
22 楼 tongjian 2008-12-04  
sdh5724 写道
MEMCACHED不适合要求数据可靠的应用, 要可靠的数据, 内存数据库是不错的选择。 

非常同意,“Keep in mind that memcached is a cache not a database. It is fast, but not reliable storage.”
所以不要认为memcached是个好东西就是万能的。
21 楼 tongjian 2008-12-04  
补充:
晕了,本来想编辑帖子,一不小心把上边帖子引用楼主的内容删了。还是再回一个吧
两点误解的补充:
第一点首先楼主说一个slab下有多个page,我理解page数指的是某一个slab的数量。
第二点我的理解是如果还有可分配的内存,则不会覆盖1号slab的数据,如果内存没有可分配的了,则会覆盖1号slab比较旧的数据。此时你说“我的内存还有40%的时候LRU就执行了”,其实此时已经没有内存可分配了,因为这40%内存已经分配给了其他号的slab,所以1号slab的旧数据会被LRU。


还有
“Keep in mind that memcached is a cache not a database. It is fast, but not reliable storage.”
不知大家怎么理解的?
20 楼 ahuaxuan 2008-12-04  
哦,还有一点忘记说了,我们把memcache扩展了之后,完全改变了LRU的行为,而且可以通过key的前缀来控制这个key是需要被LRU,当然做这些事情的前提是充分理解memcached的原理。
19 楼 ahuaxuan 2008-12-04  
tongjian 写道
认为楼主有两个概念介绍错了

如果我理解错了,希望大家拍砖,并把这个问题说清楚,不要迷惑更多的人!


你确实理解错了,事情正如我所说,不信的话你可以看memcached的源代码,既然认为我介绍错了,我也非常希望你把你的分析或者实验数据拿出来。

还有即使我错了,我想的目的也不是去迷惑其他人。本来是把自己的经验拿出来的,现在被说成了是迷惑其他人,呵呵。

当然说那么多没有用,拿证据出来证明我是错的吧

------------------------
tongjian 写道

1.page是描述slab大小的。


不管你去网上找什么资料,都是和我的描述基本上是保持一直的。
去看看memcached的原代码吧
tongjian 写道

2.第二点需要一些数据证明一下,假如我有1千万+量级的数据,数据大小都是80b, 那按楼主的意思,无论memcahced内存开多大,都最多只会用一个slab大小,即1M。 那memcached也太弱了,我对这个观点表示怀疑,找时间我会测试一下。

这一点完全你是没有理解,我在正文上早就说了,一个slab会有多个page,明白不?怎么可能只用1m(只是预先分配1m,不是说只能使用1m,当这1m的数据满了之后,会重新分配一个page给这个slab,一旦到达使用内存的上限就不会再分配新的page了,新数据进来会有针对该slab的LRU,而且这个LRU只是针对该双向链表的前50个元素),看清楚文章再发言可以吗
18 楼 sdh5724 2008-12-04  
楼主肯定是因为数据的长度大小不相同的紊乱程度造成的, 如果说还有40%的内存, 肯定还会继续分配的。  另外, 建议你对于长度超过一定大小的进行压缩。
17 楼 sdh5724 2008-12-04  
MEMCACHED不适合要求数据可靠的应用, 要可靠的数据, 内存数据库是不错的选择。 
16 楼 tongjian 2008-12-04  
认为对楼主两点说的不清楚的地方:
1.
引用
每个slab下又有若干个page

2.
引用
这就解释了为什么我的内存还有40%的时候LRU就执行了,因为我的其他slab中的chunk_size都远大于我的value,所以我的value根本不会放到那几个slab中,而只会放到和我的value最接近的chunk所在的slab中(而这些slab早就满了,郁闷了)。这就导致了我的数据被不停的覆盖,后者覆盖前者。



下面是从其他帖子里的摘录:
引用

Slab Allocation的主要术语

Page

分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。

Chunk

用于缓存记录的内存空间。

Slab Class

特定大小的chunk的组。



引用

开启memcached:
memcached -d -m 10 -l 192.168.1.21 -p 11222 -u userA
这行命令会开启memcached服务,memcached在192.168.1.21:112222上面进行监听, 同时设置memcached使用的最大内存为10MB, memcached一开始并不会一下子申请10MB的内存, 而是在需要的时候才会使用malloc申请内存,当申请内存达到10MB时就不会再申请.

为了减少管理内存碎片的麻烦,当你需要通过memcached往缓存里面保存一个数据时, memcached给这个数据提供一个固定大小的内存块(chunk),比如数据的长度是100bytes,那么memcached提供一个大小为128b的chunk来存储该数据,chunk块的大小可以为64B,128B,256B...1024KB.使用何种大小的chunk块是由memcache根据数据的长度来决定的.

当你第一次往memcached存储数据时, memcached会去申请1MB的内存, 把该块内存称为一个slab, 也称为一个page, 如果可以存储这个数据的最佳的chunk大小为128B,那么memcached会把刚申请的slab以128B为单位进行分割成8192块. 当这页slab的所有chunk都被用完时,并且继续有数据需要存储在128B的chunk里面时,如果已经申请的内存小于最大可申请内存10MB时,memcached继续去申请1M内存,继续以128B为单位进行分割再进行存储;如果已经无法继续申请内存,那么mamcached会先根据LRU算法把队列里面最久没有被使用到的chunk进行释放后,再将该chunk用于存储.

chunk属于某个slab,slabs由memcached进行分组管理,以同样chunk大小进行分割的slab属于同一组.



我的理解:
第一点首先楼主说一个slab下有多个page,我理解page数指的是memcache分配的某一个型号的slab的数量。
第二点我的理解是如果还有可分配的内存,则不会覆盖1号slab的数据,如果内存没有可分配的了,则会覆盖1号slab比较旧的数据。此时你说“ 我的内存还有40%的时候LRU就执行了”,其实此时已经没有内存可分配了,因为这40%内存已经分配给了其他号的slab,所以1号slab的旧数据会被LRU。

如果我理解错了,希望大家拍砖,并把这个问题说清楚,不要迷惑更多的人! --- 不是说楼主迷惑人,是网上各种说法迷惑人 :),至少我是花了一晚上时间看文档 测试才自认为搞清楚了。 楼主不要激动  呵呵 ,绝对支持你的分享精神


15 楼 sizhefang 2008-09-12  
谢大家对memcached的补充,不过这份资料在第一页quake wang大已经推荐过了
⇒哈哈,不好意思。没有仔细看所有人的回复,只是感觉这篇文档是目前见过的
  关于memcached的比较不错的文档。如果哪位家里人手里还有不错的文档希望
  也能共享一下,因为最近我也在搞memcached这个东东~~
14 楼 ahuaxuan 2008-09-10  
sizhefang 写道
贴一个感觉不错的memchached的系列文档

也可以通过下面的链接下载
http://bigweb.group.iteye.com/group/topic/6838

谢大家对memcached的补充,不过这份资料在第一页quake wang大已经推荐过了


----------------------------------------------------------

这份文档说是全面,其实还是不全面,因为这两个japanese并没有告诉我们LRU是针对slab的,而这一点是本文的主题,其他描述都是铺垫,因为其他描述网上到处都是,恰恰针对slab的LRU网上到处都没有,本文也可以算是对这份文档的一个补充吧

13 楼 sizhefang 2008-09-10  
贴一个感觉不错的memchached的系列文档

也可以通过下面的链接下载
http://bigweb.group.iteye.com/group/topic/6838
12 楼 fly_hyp 2008-08-25  
指出楼主的一个小错误:
   “memcached会预先分配内存”

Memcached是在需要分配内存时,分配一个page,大小为1M,page就是一个类型的slab.
当定义的内内被分完以后,Memcached不再重新分配slab.
11 楼 everlasting_188 2008-08-19  
radar 写道
我们也想大数据量放到memcached中。
需求和博主一样,把memcached当成内存数据库来使用。

问个问题,你这几千万数据怎么初始化的?



操作系统如何使用内存的,一样的道理。
10 楼 QuakeWang 2008-08-14  
谢谢好文章,推荐另外一份memcached全面剖析–2.理解memcached的内存存储:
http://tech.idv2.com/2008/07/11/memcached-002/
这是一个系列翻译文章中的一部分,可以从他那本链接到更多关于memcached的文章
9 楼 hjoo 2008-08-13  
不错,有收获.
8 楼 bbyyzhang 2008-08-13  
拜读,没用过,看了还是小有收获
7 楼 sz-James 2008-08-12  
睡醒了么?:)
我想请问一下,如果数据库里的内容已经更新了,
那缓存中的数据还有意义么?我没搞过memcached,对它也很感兴趣,
可是对他的机制没有一丝认识,希望我的提问不会被你笑话哈
6 楼 ahuaxuan 2008-08-11  
outrace 写道

Tokyo Tyrant
参看:http://blog.s135.com/read.php/362.htm


谢谢你提供的
Tokyo Tyrant的相关信息,这个东西看上去不错

radar 写道
我们也想大数据量放到memcached中。
需求和博主一样,把memcached当成内存数据库来使用。

问个问题,你这几千万数据怎么初始化的?

多线程分段初始化

相关推荐

Global site tag (gtag.js) - Google Analytics