一、Memcached内存分配机制
-
假如我们要存放的数据为 200 Bytes , 当向 memcached 存放数据时:
1, memcached先检查数据的大小, 得到数据尺寸(200 Bytes);
2, 再检查 memcached 已划分过的并且可用的并且自身大小大于数据尺寸的最小容量的 chunk ,
如果有存在这样的 chunk , 跳到第 4 步, 如果没有跳到第 3 步;
3, memcached 向内存申请 1MB 的空间并将这 1MB 的空间划分为 4096 个大小为 256 Bytes 的 chunk;
为什么是 4096 个?
1M = 1024K = 1048576B
1048576 / 256 = 4096
为什么是 256 Bytes?
memcached 根据特定的 growth factor 来对 chunk 的尺寸做增加
(默认为 1.25, 为了方便描述统一按 2 来进行计算)
例如所有的 chunk 的尺寸大小都只会下面列表中的一类, 不会出现大小为 100 Bytes 的 chunk.
64 Bytes,
128 Bytes
256 Bytes,
512 Bytes,
1024 Bytes,
2048 Bytes,
....
4, memcached 取到一个已划分过的并且可用的并且自身大小大于数据尺寸的最小容量的 chunk , 保存数据.
要注意的点:
1, 第 2 步中提到的"可用的"chunk, "可用的"是指一个空的 chunk 空间或者是一个已有数据但数据已过期的 chunk 空间.
2, 第 3 步中申请的 1MB 内存空间被称为一个 slab, 也叫一个 page
3, 以同样 chunk 大小进行分割的 slab 属于同一组 slab class
4, memcached 的 key 限制为 250 bytes
5, memcached 单个缓存数据大小最大为 1MB(1000000bytes) , 这是由 memcached 的 page 大小限制的.
6, 通过 stats slabs 可以查看各个 slab class 的统计信息
假设现在一组slab, 它的chunk大小是128KB, total_pages =1, 那么
total_chunks = (1MB/128KB) * 1 =
8,
total_chunks=used_chunks + free_chunks,
free_chunks = 被使用过但是已经释放的chunk个数(注:个人理解)
free_chunks_end = 没有被使用过的chunk个数(注:个人理解)
Page为内存分配的最小单位。
used_chunks = free_chunks_end + 现在存储着数据的chunk个数
=================================================== OR ========================================================Memcached的内存分配以page为单位,默认情况下一个page是1M,可以通过-I参数在启动时指定。如果需要申请内存时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配(page ressign已经从1.2.8版移除了)
- Slabs划分数据空间。
Memcached并不是将所有大小的数据都放在一起的,而是预先将数据空间划分为一系列slabs,每个slab只负责一定范围内的数据存储。如下图,每个slab只存储大于其上一个slab的size并小于或者等于自己最大size的数据。例如:slab 3只存储大小介于137 到 224 bytes的数据。如果一个数据大小为230byte将被分配到slab 4中。从下图可以看出,每个slab负责的空间其实是不等的,memcached默认情况下下一个slab的最大值为前一个的1.25倍,这个可以通过修改-f参数来修改增长比例。
- Chunk才是存放缓存数据的单位。
Chunk是一系列固定的内存空间,这个大小就是管理它的slab的最大存放大小。例如:slab 1的所有chunk都是104byte,而slab 4的所有chunk都是280byte。chunk是memcached实际存放缓存数据的地方,因为chunk的大小固定为slab能够存放的最大值,所以所有分配给当前slab的数据都可以被chunk存下。如果时间的数据大小小于chunk的大小,空余的空间将会被闲置,这个是为了防止内存碎片而设计的。例如下图,chunk size是224byte,而存储的数据只有200byte,剩下的24byte将被闲置。
- Slab的内存分配。
Memcached在启动时通过-m指定最大使用内存,但是这个不会一启动就占用,是随着需要逐步分配给各slab的。
如果一个新的缓存数据要被存放,memcached首先选择一个合适的slab,然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如果没有则要进行申请。slab申请内存时以page为单位,所以在放入第一个数据,无论大小为多少,都会有1M大小的page被分配给该slab。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,在从这个chunk数组中选择一个用于存储数据。如下图,slab 1和slab 2都分配了一个page,并按各自的大小切分成chunk数组。
- Memcached内存分配策略。
综合上面的介绍,memcached的内存分配策略就是:按slab需求分配page,各slab按需使用chunk存储。
这里有几个特点要注意,- Memcached分配出去的page不会被回收或者重新分配
- Memcached申请的内存不会被释放
- slab空闲的chunk不会借给任何其他slab使用
知道了这些以后,就可以理解为什么总内存没有被全部占用的情况下,memcached却出现了丢失缓存数据的问题了。