我们知道,memcached是一个内存缓存系统,因此对于内存的管理是需要使用者了解的。本文将对memcached的内存模型及管理机制做一个详细的描述。
基本概念
在开始之前,有必要先了解几个基本概念:
1、slab class:在memcached中,对元素的管理是以slab为单元进行管理的。每个slab class对应一个或多个空间大小相同的chunk。参考下图一。
2、chunk:存放元素的最小单元。用户数据item(key、value等)最终会保存在chunk中。memcached会根据元素大小将其放到合适的slab class中。每一个slab class中的chunk空间大小是一样的,所以元素存放进来后,chunk可能会有部分空间剩余。参考下图二、下图三。
3、page:大小固定为1MB。当slab class空间不足时,就会申请page,并将page按chunk的大小进行切割。
图一 slab class逻辑结构图
图二 元素存入memcached会寻找最合适的slab class
图三 元素放入chunk时可能会有空间浪费
memcached使用自己的内存管理机制,可以有效避免系统内存碎片,避免给操作系统带来负担。
内存如何分配给元素
启动memcached时,以-m指定大小的内存将会用于数据的存放。默认情况下,这些内存会被分隔成1M的page。每个page在必要时分配给slab class,然后根据slab class里chunk的大小,将page分隔成chunk。
一旦一个page被赋给一个slab class后,它将不会再被移动。因为内存空间有限,如果在slab class 3中使用了较多的page,那么在slab class 4中就只能使用较少的page。可以这么认为,memcached是一个有很多更小的相互独立的缓存系统,每一个更小的缓存系统实际上就是slab class。每个slab class都有它自己的统计信息以及自己的LRU。
在启动memcached时,指定-vv参数,可以在启动日志中查看每个slab class的chunk大小。
$ ./memcached -vv
slab class 1: chunk size 80 perslab 13107
slab class 2: chunk size 104 perslab 10082
slab class 3: chunk size 136 perslab 7710
slab class 4: chunk size 176 perslab 5957
slab class 5: chunk size 224 perslab 4681
slab class 6: chunk size 280 perslab 3744
slab class 7: chunk size 352 perslab 2978
slab class 8: chunk size 440 perslab 2383
slab class 9: chunk size 552 perslab 1899
slab class 10: chunk size 696 perslab 1506
[...etc...]
在slab class 1中,每个chunk大小为80字节,每个页面包含13107个chunk(或item)。这两个数相乘,应该最近接1个page的大小(默认是1MB)。
当存储元素时,它们将被放到最合适的slab class中。例如,50字节的元素(包含key、value等信息)将被存放到slab class 1中的chunk,根据之前的说明,此chunk中将会有30字节的空间浪费。如果存放数据总共有90字节,将被存放到slab class 2中,此时会有14字节的空间浪费。
启动时,通过设置参数-f,可以设置每个slab class下chunk的空间增长率。
另外,memcached因为一些其他功能也会使用内存空间。例如:用来查找元素的hash table;连接时也会使用一些缓存。当然,这些功能只会占用很少一部分内存空间。
内存什么时候被回收
与redis不同的是,memcached不会主动回收内存。
如果获取了一个已失效的元素,memcached将会释放内存。后续再将新的元素存放进来时,将会重用此内存。(前提是新元素也是要存放在同一slab class中。)
由于LRU,元素将被踢出以让给新的元素,或发现有过期的元素,它们的内存将被重用。
一个元素将会使用多大空间
除了数据需要占用空间,一个元素本身也占用空间:key的长度、元素的内部数据结构、数据的长度。
可以使用memcached提供的命令./sizes查看占用空间大小。
$ ./sizes
Slab Stats 56
Thread stats 176
Global stats 108
Settings 88
Item (no cas) 32
Item (cas) 40
Libevent thread 96
Connection 320
----------------------------------------
libevent thread cumulative 11472
Thread stats cumulative 11376
元素什么时候被踢出
如果元素还没有失效(失效日期为0或者将会在将来失效),slab class已经用完了所有空闲的chunk,并且没有空闲的page可以分配给此slab class,此时将会执行LRU,将元素踢出。
LRU怎么决定哪个元素被踢出
当存放新的元素时,内存也可能会被回收。如果在相应的slab class里,既没有空闲的chunk,也没有空闲的page,memcached将会使用LRU算法查找应该被回收的元素。它将会查找LRU列表中尾部(最近最少使用)的一些元素,看它们是否已经失效,如果存在失效的元素,则将此元素占用的空间重用。如果找不到失效的元素,那么将踢出最尾部还没有失效的元素。