设为首页 收藏本站
查看: 1309|回复: 0

[经验分享] memcached内存分配及回收初探

[复制链接]

尚未签到

发表于 2015-9-2 05:47:47 | 显示全部楼层 |阅读模式
  对memcached(后面简称mc) 的内存分配及回收机制进行了深度分析和测试,以下是一些学习的心得,和大家共同探讨一下,期望能抛砖引玉
  
  mc简介:
  
  mc是由LiveJournal技术团队开发的一套分布式对象缓存系统,基于c语言,目前应用十分广泛,它可以应对任意多个连接,使用非阻塞的网络I/O。它的使用非常简单和方便,最常用的功能不超过5个方法(set,get,delete...)。目前pconline的网站群基本上都是使用mc做缓存服务
  
  mc在很多时候都是作为数据库前端缓冲使用的。因为它比数据库少了SQL解析、磁盘操作等开销,而且它是使用内存来管理数据的, 所以它可以提供比直接读取数据库更好的性能,在大型bbs系统中,访问同样的数据是很频繁的,mc可以大大降低数据库压力,使系统执行效率提升。 另外,mc也经常作为服务器之间数据共享的存储媒介,例如在SSO系统中保存系统单点登陆状态的数据就可以保存在mc中,被多个应用共享。
  
  命题提出
  
        前段时间登录系统出现了一个比较怪异的问题,刚刚登录的用户,还未到session过期时间就开始抛空异常,查看mc内存利用率不到60%,检查重启mc后问题得到缓解,后来小虎分析可能是mc中缓存的未过期数据被冲掉,于是有了下面的分析
  
  mc内存分配机制简介
  memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存,Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。
  先来解释一下与Slab Allocator存储有关的几个术语:
  Page:分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。 Chunk:用于缓存记录的内存空间。 Slab Class:特定大小的chunk的组。
  Growth Factor:增长因数,默认为1.25(较早的版本固定为2)
  
  mc启动后,会根据这个factor,计算出从1M逐步递减的不同的slab,如factor=1.25时:
  



Java代码 http://rails2010.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf DSC0000.png

  • <span style="font-size: medium;">slab class   1: chunk size     88 perslab 11915  
  • slab class   2: chunk size    112 perslab  9362  
  • slab class   3: chunk size    144 perslab  7281  
  • slab class   4: chunk size    184 perslab  5698  
  • slab class   5: chunk size    232 perslab  4519  
  • slab class   6: chunk size    296 perslab  3542  
  • slab class   7: chunk size    376 perslab  2788  
  • slab class   8: chunk size    472 perslab  2221  
  • slab class   9: chunk size    592 perslab  1771  
  • slab class  10: chunk size    744 perslab  1409  
  • ...</span>  
  
  第一列数据(slab class),为slab的编号;
  第二列数据是chunk的大小,跟slab class是一一对应的关系,可以通俗的理解为slab就是存放一组相同大小chunk的集合,只不过这个集合是固定的(1M),
  第三列数据,表示每种不同slab中的page可以存放的chunk个数,实际上等于1MB/ (chunk size),例如slab1中的chunk size是88B,那么这种slab中每个page中可以存放的chunk个数为 1MB / 88B ,约等于11915
  很显然,slab的chunk size越大,其中的每个page包含的chunk数量就越少
  如图所示:
  
   DSC0001.jpg
  
  新入对象时,会根据自身大小来匹配slab列表,比如100KB的对象,根据最小空间损失原则,会被放入到slab2(size:112B)对应的page下,如下图
   DSC0002.jpg
  这时,如果slab2下的page中有尚可以使用的chunk(即空闲的chunk或者过期的chunk),slab2会优先使用这些chunk,在没有chunk可用的情况下,mc会去内存中再申请一个page,然后切分成chunk,然后使用;需要注意的是,根据 Slab Allocator算法, 该实例中的100KB对象,是永远没有机会存放到其他slab(如slab3,slab4等等),即便是其他slab中有大量的可用chunk,细心的朋友会发现,这种机制很有可能会导致内存浪费严重,mc命中率降低等问题,对,这种问题真的存在,这也正是这种机制的缺点,下面会进行详细的分析和探讨
  
  mc数据删除机制简介:
  首先我们知道,缓存在mc中的数据,不会主动从内存中消失,也就是说mc不会监视记录是否过期,而是在client端执行get方法时才去检查时间戳是否过期(这样做的目的就是避免在过期监视上耗费cpu资源,以提高mc的响应能力);每次有新对象加入时,mc会优先将对象置于已超时的同一规格chunk中,然而,即使如此,内存仍然会发生追加新记录时空间不足的情况,那么,当mc内存耗完后,又是怎样处理新入的数据呢?mc有两种处理策略,一种是默认的LRU(Least Recently Used),指删除近段时间最少使用同规格chunk,再将对象塞入),另一种策略是存满即不可再存,除非有过期的对象,否则会报错
  
  再回头看问题:
  想必各位已经发现我们的登录状态数据是怎么被冲走的了,对,没错,LRU!在内存还有将近一半的情况下,就发生了LRU,为什么呢?这一半空闲的内存,表面是空闲的,实际上已经被mc将其打包成page分配到了其他stat里,而这些stat即便空闲、数据过期,也不会被mc回收已供其他繁忙的slab调用的。产生这种情况的直接原因就是,mc启动后,较大chunk size的slab同时间大量涌入mc,假设slab为20,这时,mc不得为slab 20分配大量的page,而在一小段时间后,slab 20中的chunk纷纷过期,但是它们曾经占用的page就永远不会被mc主动回收了,除非再有与slab20同规格的对象进入时,这些page才会重新得到使用的机会,与此形成强烈对比的slab2(chunk size = 112B)却处于无可用chunk,无内存已供分配新page的境地,这个时候,LRU出场了,会按时间相关度清理掉一些尚未过期的slab2 chunk,如此造成缓存来去匆匆,实际上性能严重下降
  
  LRU:
  首先,在memcache分配的时候,初始化会去分配一系列的slab,例如初始的slab为88k,然后factor为1.25,那么你会发现开始的时候 就会有:88,112,44,....一直到1M大小的slab各一个,假如对象集中在其中某一个区间,那么很快那个slab就会分配满,此时如果内存还有,那么就会新建一个同样大小的slab作为链挂在第一个同等大小的slab上,如果说内存也满了,slab也满了,那么就开始LRU算法了。  但是Memcached的LRU算法是针对slab的,而非全局的,如果数据集中在一个slab上,那么初始化的时候其他几个slab肯定就浪费了,同时,如果slab的大小和对象的大小有比较大的差异,那么浪费的将会更加巨大。所以在评估使用 memcache初始大小和factor的时候需要注意这些,选择适合的初始化size和factor,减少slab分配的浪费。
  
  解决思路
  
  思路1.通用解决方法: 调整growth factor
  逐步调整growth factor,并观察chunks的分布,尽量将数据对象的大小控制到一定区间内,启动时加入-f参数即可,在factor=1.25时有39组slab
  



Java代码 http://rails2010.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf

  • $memcached  -m 5 -vv  -l localhost  -p 11211 -f 1.25s  
  •   
  • slab class   1: chunk size     88 perslab 11915  
  • slab class   2: chunk size    112 perslab  9362  
  • slab class   3: chunk size    144 perslab  7281  
  • slab class   4: chunk size    184 perslab  5698  
  • slab class   5: chunk size    232 perslab  4519  
  • slab class   6: chunk size    296 perslab  3542  
  • slab class   7: chunk size    376 perslab  2788  
  • slab class   8: chunk size    472 perslab  2221  
  • slab class   9: chunk size    592 perslab  1771  
  • slab class  10: chunk size    744 perslab  1409  
  • slab class  11: chunk size    936 perslab  1120  
  • slab class  12: chunk size   1176 perslab   891  
  • slab class  13: chunk size   1472 perslab   712  
  • slab class  14: chunk size   1840 perslab   569  
  • ...  
  • slab class  36: chunk size 250376 perslab     4  
  • slab class  37: chunk size 312976 perslab     3  
  • slab class  38: chunk size 391224 perslab     2  
  • slab class  39: chunk size 489032 perslab     2  
  
  当growth factor调大成2以后,slab class明显变少,只有13组了
  



Java代码 http://rails2010.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf

  • $memcached  -m 5 -vv  -l localhost  -p 11211 -f 2  
  •   
  • slab class   1: chunk size    128 perslab  8192  
  • slab class   2: chunk size    256 perslab  4096  
  • slab class   3: chunk size    512 perslab  2048  
  • slab class   4: chunk size   1024 perslab  1024  
  • slab class   5: chunk size   2048 perslab   512  
  • slab class   6: chunk size   4096 perslab   256  
  • slab class   7: chunk size   8192 perslab   128  
  • slab class   8: chunk size  16384 perslab    64  
  • slab class   9: chunk size  32768 perslab    32  
  • slab class  10: chunk size  65536 perslab    16  
  • slab class  11: chunk size 131072 perslab     8  
  • slab class  12: chunk size 262144 perslab     4  
  • slab class  13: chunk size 524288 perslab     2  
  
  很显然,factor越小,chunk匹配得就越精准,但是slab组就会分得越多,而产生LRU的机会也会增加,factor越大,分组就越少,产生LRU的机会就越小,但是chunk匹配精准度会有所下降,如在数据大小为130B时,如果f=1.25,mc会将其放入class3(chunk size = 144B),浪费的空间为14B;如果f=2.0,mc会将其放入class2(class size = 256)中,浪费的空间为126B,相当惊人,所以factor的大小设置在一个比较平衡的值,一般以默认的1.25较为理想。
  
  思路2.动态调整slab中page的数量
  大体思路是使用java客户端监控程序,定时检查每个slab的使用情况,动态调整每个slab中的page,将某个比较空闲的slab中的page移动到另外的slab中去,不过mc的开发人员认为在mc中遍历slab和page移动会造成较大系统开销,所有没有提供直接的api已供调用,一直屏蔽调用,在1.28版本以前,还可以通过在memcached.h中添加宏#define ALL_SLABS_REASIGN并重新编译使slabs reassign命令生效,但在使用过程中,出现了大量的性能问题,mc稳定性下降,而且在数据移动过程中,会导致mc不可写的问题,针对这些弊端,mc的开发团队在其后续版本中已经彻底删除相关处理逻辑,我也尝试对1.28的源码进行修改和编译,基本可用,但是何时调用 reassign,以及reassign后对系统造成的影响仍然需要进一步的数据分析,继续跟进中。。。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-108460-1-1.html 上篇帖子: CentOS或Redhat上装memcached (包括64位系统) 下篇帖子: 服务器安装Apache+Tomcat+Memcached共享Session的构架设计
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表