谈一下 Memcached LRU

谈谈 Memcached LRU

一. Memcached 内存结构

谈一下 Memcached LRU

可以看出 Memcached 将内存分为一个一个 slab class,slab class 没有大小,所有的 slab class 加起来就是 Memcached 启动时设置的内存大小。slab class 里面有 slab page,slab class 大小由 slab page 个数决定,默认的 slab page 为 1M,可以在启动 Memcached 时设置。slab page 里又分为同等大小的 chunk,chunk 就是存储缓存的基本单元,chunk 大小,不同 slab class 的 page 大小一致,但是 chunk 大小不一致,默认 class1 的 chunk 大小为 96 byte,不同 class 的 chunk 大小由启动时设置的 factor 系数决定,下一个 class 的 chunk 是上一个 class 的 chunk 的 1.25 倍(默认是 1.25)。


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


      Slab Allocator的基本原理是按照预先规定的大小,将分配的内存以page为单位,默认情况下一个page是1M,可以通过-I参数在启动时指定,分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合),如果需要申请内存时,memcached会划分出一个新的page并分配给需要的slab区域。page一旦被分配在重启前不会被回收或者重新分配,以解决内存碎片问题。



二. 如何分配

Memcached在启动时通过-m参数指定最大使用内存,但是这个不会一启动就占用完,而是逐步分配给各slab的。如果一个新的数据要被存放,首先选择一个合适的slab(存入的数据大小和 chunk 大小最接近,这样可以减少内部碎片的浪费),然后查看该slab是否还有空闲的chunk,如果有则直接存放进去;如果没有则要进行申请,slab申请内存时以page为单位,无论大小为多少,都会有1M大小的page被分配给该slab(该page不会被回收或者重新分配,永远都属于该slab)。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk的数组,再从这个chunk数组中选择一个用于存储数据。若没有空闲的page的时候,则会对改slab进行LRU,而不是对整个memcache进行LRU。(PS:局部 LRU)



三. 面临的问题

在这样的内存结构和内存分配的策略上,会有一些典型的问题,比如 Memcached 还有一般的空闲内存,但是进行了 LRU,原因就是某些缓存数据大小相近的都是分配在某个 slab class 上,这些数据又是热数据,又不断有加入的新数据,因为是局部 LRU,所以导致在这个 slab class 上频繁进行 LRU,但是其他 slab class 里面却有大量空闲空间。这样的情况如果频繁,可以考虑调小 factor,这样大小类似的数据可能会分配到不同的 slab class 上,减少 LRU 情况。