memcached的一些研究(关于memcached的内存分配机制)

来源:互联网 发布:淘宝主图psd模版 编辑:程序博客网 时间:2024/05/29 18:01

memcached作为缓存已被应用的非常多,memcached的数据结构非常简单,就是key-value的存储,了解下memcached的内存分配机制有助于更好的使用memcache

memcached相关的内存术语

  • chunk:数据是存储在称为chunk的内存空间里的
  • page:已有缓存空间满了以后,每次会申请一个page(默认一个page为1M),page包含成多个内存大小相等的chunk
  • slab class:slab有很多相同的chunk,slab是根据具体的缓存情况决定大小的;每次申请的page都在slab下;
  • 具体的模型类似下图:这里写图片描述

内存分配机制

memcached的内存分配机制决定了memcached的优缺点,比如不适合存储比较大的数据,对小数据存储非常快

memcached内存采用slab allocator机制

那什么是slab allocator?(以启动配置为:增长因子为2,page大小为1M)

  • memcached能管理的内存通过参数 -m 100m来设置(这里指定了100M)
  • memcached根据page大小,来对slab进行分配,在page大小为1M,增长因子为2的情况下,分配slab为:第一个slab的chunk大小为96B(默认以96B开始),第二个slab的chunk大小为192B(96B*2),以此类推,一直到1M大小(你会发现最后一个跟最后第二个的大小不是呈现*2的规律,这个在后文再解释),所有slab的个数是由page大小和增长因子决定的
  • 执行 memcached -f 2 -vv的结果:

    slab class   1: chunk size        96 perslab   10922slab class   2: chunk size       192 perslab    5461slab class   3: chunk size       384 perslab    2730slab class   4: chunk size       768 perslab    1365slab class   5: chunk size      1536 perslab     682slab class   6: chunk size      3072 perslab     341slab class   7: chunk size      6144 perslab     170slab class   8: chunk size     12288 perslab      85slab class   9: chunk size     24576 perslab      42slab class  10: chunk size     49152 perslab      21slab class  11: chunk size     98304 perslab      10slab class  12: chunk size    196608 perslab       5slab class  13: chunk size    393216 perslab       2slab class  14: chunk size   1048576 perslab       1....

    一共分配了14个slab class,最后一个和倒数第二个并不呈现增长因子的规律

  • 可以看到在启动的时候,这14个slab class都分配到最接近1M大小的内存,这里并不是完整的1M,只有slab class 14是1M;

  • 那数据如何选择存储在哪个slab class下的chunk中呢?答案是能存储下需要存储数据大小的最小的slab class的chunk中;举个栗子:有slab class 1(96B),slab class 2(192B),slab class 3(384B),….;那么94B的数据就存储在slab class 1,98B的数据需要存储在slab class 2中,换句话说就是:slab class 1存储0~96B的数据,slab class 2存储97B~192B的数据,slab class 3存储193B~384B的数据,以此类推;这样就会发现,极端情况下会有将近50%的存储空间浪费(slab class 1都存储小于1B的数据,slab class 2都存储97B的数据,……);但真实情况不会有这么多,但肯定是会有空间浪费的,因为不会出现另一个极端(slab class 1都存储小于96B的数据,slab class 2都存储192B的数据,……);所以这里就是一个调优的点了,需要根据实际应用中需要缓存的数据大小分布来进行增长因子的调整;(合理设置增长因子能够减少内存的浪费)
  • 当slab class中的chunk分配完了,那怎么办?这里分2中情况

    • memcached管理的内存还有超过1个page的大小可以分配,那么就分配一个page给slab class,继续存储数据
    • 如果没有足够内存继续分配了,默认情况下采用LRU(latest recent use)算法进行缓存失效;也可以配置成FIFO(first in first out)算法;也可以配置成不进行缓存失效,这样就存储不了数据了,memcached返回存储失败
  • memcached在set的时候需要设置缓存失效时间,但缓存时间到了,memcached并不会把数据给抹掉,只是在get的时候去检测缓存是否失效,这种失效机制成为lazy expire,这种做法去掉了系统监视缓存失效的开销,大大提高了性能

一些细节问题

  • 为什么memcached分配内存的时候,需要用多少分配多少呢?

    如果这样做,那就是实时进行内存分配了,那么很多的开销都花在了内存的分配及回收上了,性能就会大打折扣;预先分配可以减免这方面的开销;其实这就是典型的以空间换取时间的做法

  • 经常有人问,为什么我往memcached存储超过1M

    因为memcached默认的page大小刚好为1M,而chunk是由page(即1M大小分拆成n个chunk,最大的chunk也就是1M),而数据存储在chunk中,当然存储不了超过1M的数据;如果非得存储超过1M的数据,那么只能去修改page的大小了,具体怎么修改,网上应该有很多;memcached非常适合存储数据比较小的缓存,当数据比较大的时候,如存储的数据都解决page的大小的情况下,会进行频繁的内存分配操作,性能就会降下来

  • 上面提到的一个问题:为什么最后一个slab class的chunk大小为1M,而最后第二个slab不是512K呢?

    memcached为了保证能存储的最大的数据刚好为一个page(这里是1M)的大小,在分配算法上做了处理,就是当倒数第二个slab class的chunk大小乘以增长因子不到1M,而倒数第二个slab class的chunk大小乘以增长因子再乘以增长因子超过1M,那么直接就分配最后一个为1M的slab;

  • 细心的同学可能会发现,增长因子设置以后,slab class的chunk内存空间增长并不是按照前一个slab class的chunk乘以增长因子的

    对于这个问题没有去研究;在这里根据实验表现来推测:

    如果设置增长因子为1.0104,总共有63个slab class,且最后一个slab class的chunk大小为1M,其他均为96B;

    如果增长因子设置为1.0105,也是有63个slab class,最后一个为slab class的chunk大小为1M,其他为递增的方式,第一个为96B,第二个未104B,第三个为112B;

    如果增长因子设置为1.084,也是有63个slab class,最后一个为slab class的chunk大小为1M,其他为递增的方式,第一个为96B,第二个未104B,第三个为112B;

    如果增长因子设置为1.1,也是有63个slab class,最后一个为slab class的chunk大小为1M,其他为递增的方式,第一个为96B,第二个未112B,第三个为112B;

    发现规律过程:96B乘以1.0104=96.9984;96B乘以1.0105=97.008;96B乘以1.084=104.064;96B乘以1.1=105.6;可以看出memcached最多有63个slab class;另外推测,如果当前chunk大小乘以增长因子的整数部分跟当前chunk相等,那么将当前slab class的chunk大小设置为下一个slab class的chunk大小,直到最后一个slab class(第63个),这种情况也就是在第一个slab class的chunk大小乘以增长因子小于97B的时候会发生;再看增长因子为1.0105的时候,第二个slab class的chunk大小为104,增长因子为1.084的时候,第二个slab class的chunk大小为104,增长因子为1.1的时候,第二个slab class的chunk大小为112,这里可以发现另外一个规律:增长每次都是按照至少8B的跨度进行增长的;

    具体为什么这样设计,我也不清楚;上面的结论也是靠猜的,具体的还是需要查看memcached的源码,看看到底是如何分配内存的;

其他

  • 关于memcached如何存储key的,为什么性能能如此之高,号称set和get操作的时间复杂度都是O(1),这个非常值得研究,后续会继续研究memcached的set、get等操作为何会如此之快,等研究完再进行分享
原创粉丝点击