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

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

    Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合)(图1

      

      

图1 Slab Allocation的构造图而且,slab allocator还有重复使用已分配的内存的目的。 也就是说,分配到的内存不会释放,而是重复利用

   对于slab有几个术语:

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

 chunk:用于记录缓存的内存空间

Slab class:由特定大小的chunk块组成

slab 中的缓存记录原理:

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

    

实际上,Slab Allocator也是有利也有弊。下面介绍一下它的缺点。

  

Slab Allocator解决了当初的内存碎片问题,但新的机制也给memcached带来了新的问题。

这个问题就是,由于分配的是特定长度的内存,因此无法有效利用分配的内存。 例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了(图3)。

 


对于该问题目前还没有完美的解决方案,但在文档中记载了比较有效的解决方案。就是说,如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的数据的情况下, 只要使用适合数据大小的组的列表,就可以减少浪费。

但是很遗憾,现在还不能进行任何调优,只能期待以后的版本了。 但是,我们可以调节slab class的大小的差别。 接下来说明growth factor选项。

memcached在启动时指定 Growth Factor因子(通过-f选项), 就可以在某种程度上控制slab之间的差异。默认值为1.25。 但是,在该选项出现之前,这个因子曾经固定为2,称为“powers of 2”策略。

 $memcached -f 1.25  (-f选项可以用来调整slab增长的因子)

查看slab的使用状态,需要下载memcached-tool脚本:

perl memcached-tool ip:port
  #  Item_Size   Max_age  1MB_pages Count   Full?
   1      96 B   205654 s       1      44      no
   2     120 B  1337261 s      12     163     yes
   3     152 B   302966 s       3    5474     yes
   4     192 B  1328685 s       5      62     yes
   5     240 B   977587 s       3     158      no
   6     304 B  1304414 s       8    1667     yes
   7     384 B  1337261 s       5      17      no
   8     480 B  1337262 s       2      16      no
   9     600 B  1337261 s       3      19      no
  10     752 B  1337263 s       5       9      no
  11     944 B  1328488 s       4      10      no
  12     1.2 kB 1328486 s       4      16      no
  13     1.4 kB 1326990 s       3       8     yes
  14     1.8 kB 1327343 s       4       5      no
  15     2.3 kB 1326990 s       5       6      no
  16     2.8 kB  364375 s       8     672      no
  17     3.5 kB 1326990 s       4       6      no
  18     4.4 kB 1327343 s       3       2      no
  19     5.5 kB 1327310 s      11       2      no
  20     6.9 kB 1327343 s      10       1     yes
  21     8.7 kB  364430 s       7     413      no
  22    10.8 kB 1320170 s       1       3      no
  23    13.6 kB 1324719 s       2       3      no
  24    16.9 kB 1327341 s       1       1      no
  25    21.2 kB  891471 s       1       1      no
  26    26.5 kB 1312631 s       1       7      no
  27    33.1 kB  286817 s       1       1      no
  28    41.4 kB  954206 s       1       1      no
  29    51.7 kB  959350 s       1       1      no
  30    64.7 kB 1328487 s       1       1      no
  31    80.9 kB 1318688 s       1       2      no
  32   101.1 kB       0 s       1       0      no
  33   126.3 kB  644800 s       1       1      no
  34   157.9 kB  957763 s       1       2      no
  35   197.4 kB 1318686 s       1       1      no
  36   246.8 kB 1312627 s       1       2      no
  37   308.5 kB 1326996 s       1       2     yes
  38   385.6 kB  314722 s       1       1      no


参数说明:

#

slab class编号

Item_Size

Chunk大小

Max_age

LRU内最旧的记录的生存时间

1MB_pages

分配给Slab的页数

Count

Slab内的记录数

Full?

Slab内是否含有空闲chunk

从这个脚本获得的信息对于调优非常方便,强烈推荐使用。


 有关slab的详细的信息统计可以查看stats slabs命令(先要执行telnet ip port),下面给出slabs的信息:

Escape character is '^]'.
 stats slabs
 STAT 1:chunk_size 96
 STAT 1:chunks_per_page 10922
 STAT 1:total_pages 1
 STAT 1:total_chunks 10922
 STAT 1:used_chunks 73
 STAT 1:free_chunks 3884
 STAT 1:free_chunks_end 6965
 STAT 1:mem_requested 26863
 STAT 1:get_hits 4695884
 STAT 1:cmd_set 42248
 STAT 1:delete_hits 0
 STAT 1:incr_hits 0
 STAT 1:decr_hits 0
 STAT 1:cas_hits 0
 STAT 1:cas_badval 0

给出参数说明:

chunk_size:chunk的大小;

chunks_per_page:1M/chunk_size

total_pages:分配的页数

total chunks:总共的chunk数量

used_chunks:当前正在用的chunk数量

free_chunks:可以用的chunk数量

free_chunks_end :已经分配但是没有使用的chunk数量

mem_requested :实际占用的内存大小

get_hits :命中的次数

从这个slab的块可以看出每个chunk大小是96b,在其高峰时存储了10922个,接近于1m,但目前实际占用内存是26863b=0.02m,而实际分配的内存是1m,其中有6965*96/1024*1024=0.63m是无法使用的。前面说过memcache分配内存后是无法回收的,对于这0.63m已经分配但无法使用的情况是很浪费的(其他节点可能需要的chunk不是96b的,因此是不会分配到96b的slab中的),此时就造成了内存的浪费。对于上面的slab浪费还算是小的,试想如果我们线上的某一个slab分配了1g,但随着热点转移,都存储到其他节点,而原节点失效且也没有其他适合的数据块进行利用的话,那么就会出现1g的内存已经分配,但是无法使用,我们线上就出现过此种情况。

上面是进行查看slab的情况,下面的命令可以进行查看slab里面的item的情况

stats items
STAT items:1:number 41
 STAT items:1:age 205654
 STAT items:1:evicted 0
 STAT items:1:evicted_nonzero 0
 STAT items:1:evicted_time 0
 STAT items:1:outofmemory 0
 STAT items:1:tailrepairs 0
 STAT items:1:reclaimed 25646
 STAT items:2:number 262
 STAT items:2:age 1486437
 STAT items:2:evicted 690

前面的编号是代表这对应slab的编号,number代表这当前slab存储的chunk个数(1号slab对应的chunk是96b)

evicted 被去除的个数,evicted_time最后被剔除的缓存对象时间

reclaimed因为超时而被回收的次数。如果evicted的个数很大,说明需要经常剔除,那么就是节点分配的内存不够,若reclaimed的个数很大,则说明经常会有超时现象,那么缓存的时间应该设置长些。

 我们可以通过图形来进行监控memcache的使用状况,可以在网上下载php编写memcache图形监控工具,然后修改memcache.php的ip和端口配置,放到php服务器上进行运行,即可看到memcached的图形监控。