Memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存,解决了内存碎片问题。
Slab Allocator的基本原理是按照预先规定的大小,将分配的内存以page为单位,默认情况下一个page是1M(可以通过-I参数在启动时指定),分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合),如果需要申请内存时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配,以解决内存碎片问题。
slab的内存分配具体过程如下:
Memcached在启动时通过-m参数指定最大使用内存,但是这个不会一启动就占用完,而是逐步分配给各slab的。如果一个新的数据要被存放,首先选择一个合适的slab,然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如果没有则要进行申请,slab申请内存时以page为单位,无论大小为多少,都会有1M大小的page被分配给该slab(该page不会被回收或者重新分配,永远都属于该slab)。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,再从这个chunk数组中选择一个用于存储数据。若没有空闲的page的时候,则会对该slab进行LRU,而不是对整个memcache进行LRU。
概况来说:每个slab被划分成多个page,每个page又被划分为多个chunk。每个slab实际上是相同尺寸的chunk的集合。
每个slab中的chunk大小是根据增长因子递增,默认增长因子=1.25,增长因子用于当前slab都不适合存储的情况下,创建新的slab。计算的方式:新的chunk大小 = (当前最大size的chunk) * factor。增长因子可以通过启动时-f参数设置。
Page
分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。
Chunk
用于缓存记录的内存空间。(包括键、值、过期时间等)
Slab Class
特定大小的chunk的组。
Memcached并不是将所有大小的数据都放在一起的,而是预先将数据空间划分为一系列slabs,每个slab只负责一定范围内的数据存储。memcached根据收到的数据的大小,选择最适合数据大小的slab。memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk,然后将数据缓存于其中。
每个slab只存储大于其上一个slab的size并小于或者等于自己最大size的数据。例如100byte的数据,存放在slab class:2中的chunk中
Slab Allocator解决了当初的内存碎片问题,但新的机制也给memcached带来了新的问题:
1、无法有效利用分配的内存
2、当chunk设置不合理时,命中率会降低
内存并没有使用完,但是已经进行LRU排除了,因为有些slab的chunk分配过大,把内存占用(page分配的内存不会被释放),频繁使用的slab就没有内存分配了,所以该slab就进行LRU,造成命中率降低
详情(包括状态和性能查看):Memcache 内存分配策略和性能(使用)状态检查