memcache 的内存存储

Slab Allocation机制

memcached默认情况下采用了名为Slab Allocator的机制来分配、管理内存。 在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。 但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下, 会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。

slab allocator的原理

the primary goal of the slabs subsystem in memcached was to eliminate memory fragmentation issues totally by using fixed-size memory chunks coming from a few predetermined size classes.

Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块, 以完全解决内存碎片问题。

Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合),如下图,而且,slab allocator可以重复使用已分配的内存。 也就是说,分配到的内存不会释放,而是重复利用。

系统分配给Slab的内存空间称为page,默认是1MB。分配给Slab之后根据slab的大小切分成chunk,chunk用于缓存记录,特定大小的chunk组称为slab class。

memcached根据收到的数据的大小,选择最适合数据大小的slab,如下图。memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk, 然后将数据缓存于其中。

Slab Allocator的缺点

从slab的原理就知道,内存空间还是不能100%的得到利用,比如对于一个100 bytes的数据,slab会选择最合适的chunk大小为112bytes,这回造成12bytes的内存空间浪费。

对于这个缺点文档里有一个说明:

The most efficient way to reduce the waste is to use a list of size classes that closely matches (if that’s at all possible) common sizes of objects that the clients of this particular installation of memcached are likely to store.

就是说,如果预先知道客户端发送的数据的大小,或者仅缓存大小相同的数据, 或者只要使用适合数据大小的组的列表,就可以减少浪费。当然实际的情况是很难做到以上几点,所以也只能通过参数调整来达到尽量少的浪费内存。

使用参数进行调优 

和内存优化相关的参数大致有三个,分别是chunk大小的增长因子(Growth Factor),chunk大小的初始值以及slab page的大小,下面分别详细介绍这三个参数:

Growth Factor

Growth Factor的值决定了chunk的大小按怎样的倍数进行增长,memcached在启动时可以通过-f参数指定 Growth Factor值, 就可以在某种程度上控制slab之间的差异。默认值为1.25。 但是,在该选项出现之前,这个因子曾经固定为2,称为“powers of 2”策略,下面以verbose模式启动memcached:

$ memcached -f 2 -vv 

下面是输出结果:

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

可见,从96字节的组开始,组的大小依次增大为原来的2倍。 这样设置的问题是,slab之间的差别比较大,有些情况下就相当浪费内存。 因此,为尽量减少内存浪费,追加了growth factor这个选项。在使用memcached时,或是直接使用默认值进行部署时, 最好是重新计算一下数据的预期平均长度,调整growth factor, 以获得最恰当的设置,避免内存的大量浪费。

chunk大小的初始值

64位机情况下,默认memcached把slab分为42类(class1~class42),在class 1中,chunk的默认大小为96字节,由于一个slab的大小是固定的1048576字节(1M),因此在class1中最多可以有10922个chunk:10922×96 + 64 = 1048576。在class1中,剩余的64字节因为不够一个chunk的大小(96byte),因此会被浪费掉。每类chunk的大小有一定的计算公式的,假定i代表分类,class i的计算公式如下:

chunk size(class i) :  (default_size+item_size)*f^(i-1)+ CHUNK_ALIGN_BYTES
default_size: 默认大小为48字节,也就是memcached默认的key+value的大小为48字节,可以通过-n参数来调节其大小;
item_size: item结构体的长度,固定为48字节。default_size大小为48字节,item_size为48,因此class1的chunk大小为48+48=96字节;
CHUNK_ALIGN_BYTES是一个修正值,用来保证chunk的大小是某个值的整数倍。
下面使用-n参数将chunk的初始值大小设置为80字节:

$ memcached -n 80 -vv 
输出:

slab class   1: chunk size       128 perslab    8192 
slab class   2: chunk size       160 perslab    6553 
slab class   3: chunk size       200 perslab    5242 
slab class   4: chunk size       256 perslab    4096 
slab class   5: chunk size       320 perslab    3276 
slab class   6: chunk size       400 perslab    2621 
slab class   7: chunk size       504 perslab    2080 
slab class   8: chunk size       632 perslab    1659 
slab class   9: chunk size       792 perslab    1323 
slab class  10: chunk size       992 perslab    1057
slab class  11: chunk size      1240 perslab     845

可以看见class1的chunk大小为:80+48字节,根据具体的业务预估缓存数据的最小值以便设置memcache的chunk初始值,避免内存浪费。

page大小

memcache默认的page大小是1M,所以不能存入大小超过1M的数据,但一旦需要存入大数据时可以使用-I参数来设置page的值,比如我将page值设为0.5M(不推荐将page值设置为超过1M):
$ memcached -I 524288 -vv 
输出:
slab class   1: chunk size        96 perslab    5461 
slab class   2: chunk size       120 perslab    4369
slab class   3: chunk size       152 perslab    3449
slab class   4: chunk size       192 perslab    2730
slab class   5: chunk size       240 perslab    2184
slab class   6: chunk size       304 perslab    1724
slab class   7: chunk size       384 perslab    1365
slab class   8: chunk size       480 perslab    1092
slab class   9: chunk size       600 perslab     873 

以上介绍了memcache内存配置的三个参数,根据业务灵活的配置能大大的提高内存使用率。

查看memcached的内部状态

memcached有个名为stats的命令,使用它可以获得各种各样的信息。 执行命令的方法很多,用telnet最为简单:

$ telnet 主机名 端口号 

连接到memcached之后,输入stats再按回车,即可获得包括资源利用率在内的各种信息。 此外,输入”stats slabs”或”stats items”还可以获得关于缓存记录的信息。 结束程序请输入quit。下面查看slabs的适用情况

stats slabs

结果如下:

属性说明,数据操作(get、set等)相关的属性就不说明了:

chunk_size当前slab chunk的大小
chunk_per_page每个page能够存放的chunk数
total_pages分配给当前slab的page总数
total_chunks当前slab最多能够存放的chunk数,应该等于chunck_per_page * total_page
used_chunks已经被占用的chunks总数
free_chunks过期数据空出的chunk里还没有被使用的chunk数
free_chunks_end新分配的但是还没有被使用的chunk数

used_chunks, free_chunks, free_chunks_end这三个属性需要特别的关注,这三个属性可以反映出内存的适用情况。used_chunks就是字面的意思,已经使用的chunk数;free_chunks却不是所有的未被使用的chunk数,而是曾经被使用过但是因为过期而被回收的chunk数;free_chunks_end是page中从来没有被使用过的chunk数。如果free_chunks_end的数过大,表明这部分slab内存不能有效的利用;如果过小表明很快就不够用了;两种情况都需要做调整来使内存达到合理的利用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值