Memcached 的内存模型是其高效性能的核心之一。它使用了一种基于 slab 的内存分配机制,将内存划分为多个固定大小的块(slab),并通过 LRU(最近最少使用)算法管理内存。

Slab 分配机制

Memcached 使用 slab 分配器来管理内存,这是一种高效的内存分配方式,避免了频繁的内存碎片问题。

Slab 的基本概念

  • Slab:Memcached 将内存划分为多个 slab,每个 slab 是一个固定大小(1MB)的内存块;

  • Chunk:每个 slab 被进一步划分为多个相同大小的 chunk,chunk 是存储键值对的最小单位;

  • Slab Class:Memcached 中有多个 slab class,每个 slab class 对应一种 chunk 大小。chunk 大小从最小(如 80 字节)逐渐递增(通过增长因子控制)。

Slab 的工作流程

  • 内存初始化

    • Memcached 启动时,将可用内存划分为多个 slab class,每个 slab class 包含多个 slab;

    • 每个 slab 被划分为多个相同大小的 chunk。

  • 数据存储

    • 当存储一个键值对时,Memcached 会根据数据大小选择合适的 slab class;

    • 从对应的 slab 中分配一个 chunk 来存储数据。

  • 内存回收

    • 当数据过期或被删除时,chunk 会被标记为空闲,供后续使用。

Slab 的动态分配

  • 按需分配

    • Memcached 不会在启动时预先分配所有 slab,而是根据需要动态分配 slab;

    • 当某个 slab class 的 chunk 用完时,Memcached 会从空闲内存中分配一个新的 slab 给该 slab class。

  • Slab 重新分配

    • 如果某个 slab class 的内存使用率较低,而另一个 slab class 的内存不足,Memcached 可以通过 slab reassign 机制将一个 slab 从一个 slab class 转移到另一个 slab class。

Slab Class 和 Chunk 大小

  • Chunk 大小增长

    • Memcached 通过增长因子(-f 参数,默认 1.25)控制 chunk 大小的增长;

    • 例如,如果最小 chunk 大小为 80 字节,增长因子为 1.25,则后续 slab class 的 chunk 大小分别为 100 字节、125 字节等。

  • 选择合适的 Slab Class

    • 存储数据时,Memcached 会选择能够容纳数据的最小 chunk;

    • 如果数据大小超过最大 chunk 大小,Memcached 会拒绝存储(除非启用 -I 参数调整最大 item 大小)。

LRU 算法

Memcached 使用 LRU(最近最少使用)算法管理内存。当内存不足时,Memcached 会优先淘汰最近最少使用的数据。

  • LRU 队列

    • 每个 slab class 维护一个 LRU 队列,记录 chunk 的使用情况;

    • 当需要分配新的 chunk 时,Memcached 会从 LRU 队列的尾部淘汰数据。

  • 惰性删除

    • Memcached 不会立即删除过期数据,而是在访问时检查数据的过期时间;

    • 如果数据已过期,则将其标记为可回收。

内存碎片

由于 Memcached 使用固定大小的 chunk,可能会导致内存碎片问题。为了减少内存碎片,可以通过调整增长因子(-f 参数)来优化 chunk 大小的分布。

  • 内部碎片:如果数据大小小于 chunk 大小,剩余的空间会被浪费;

  • 外部碎片:不同 slab class 之间的内存分配可能导致外部碎片。

内存管理参数

Memcached 提供了一些参数来调整内存模型的行为:

  • -m:设置最大内存使用量(单位为 MB);

  • -f:设置增长因子,控制 chunk 大小的增长;

  • -n:设置每个 chunk 的最小空间(用于存储键和元数据);

  • -I:设置最大 item 大小(默认 1MB,最大可调整为 128MB)。

内存模型示例

假设 Memcached 的配置如下:

  • 最大内存:64MB;

  • 最小 chunk 大小:80 字节;

  • 增长因子:1.25。

则可能的内存分配如下:

  • Slab Class 1:chunk 大小 = 80 字节;

  • Slab Class 2:chunk 大小 = 100 字节;

  • Slab Class 3:chunk 大小 = 125 字节;

  • ...

当存储一个 90 字节的键值对时,Memcached 会选择 Slab Class 2(100 字节),并分配一个 chunk 来存储数据。