Memcached:高性能分布式内存对象缓存系统‌

Memcached:高性能分布式内存对象缓存系统

一、引言

Memcached 是一款开源的高性能分布式内存缓存系统,旨在通过将数据缓存在内存中来减少后端数据库访问次数,从而提升 Web 应用的响应速度与并发能力。自 2003 年由 Brad Fitzpatrick 开发以来,Memcached 已广泛应用于各大互联网公司,是构建可扩展、高可用架构的重要组成部分。

本文将从以下几个方面介绍 Memcached:

  1. 核心原理与架构
  2. 部署与集群拓扑
  3. 客户端应用:常见语言示例
  4. 一致性哈希与扩缩容策略
  5. 缓存失效与淘汰策略
  6. 性能优化与运维注意事项

二、核心原理与架构

2.1 基本原理

  • 内存存储
    Memcached 将数据以 <key, value> 形式缓存到 RAM 中,读取非常迅速。所有数据存储在进程内存中,没有磁盘落盘操作,因此延迟极低。
  • 纯 KV 接口
    Memcached 提供简单的文本协议与二进制协议(Binary Protocol),客户端可通过 set / get / delete 等命令进行操作。示例如下:

    set user:123 0 60 24\r\n
    {"name": "Alice", "age": 30}\r\n
    get user:123\r\n

    以上示例将 key=user:123 的值设置为一段 JSON 字符串,有效期 60 秒,长度 24 字节。

2.2 内部数据结构

  • Slab Allocator
    为避免频繁的内存碎片,Memcached 使用 slab 分配器将内存划分为不同大小的 slab class(例如 64B、128B、256B、512B……)。当存储某个对象时,Memcached 会根据 object size 选择最合适的 slab class,从而减少碎片化并提高内存利用率。
  • Hash Table
    Memcached 在每个实例内部维护一个哈希表,以便O(1) 时间完成 key 到内存地址的映射。哈希表使用拉链法解决冲突,同时配合 slab allocator 管理对象内存。

2.3 分布式架构

  • Memcached 本身并不支持多活或主从复制,每个实例是独立的。分布式是通过客户端一致性哈希Ketama等算法,将 key 映射到不同实例上,形成一个逻辑上的集群。如“图1”所示,ClientA/B/C 根据哈希后,分别将请求发送到最合适的服务器(Server1/Server2/Server3)。
  • 无中心节点:整个体系中没有集中式的 Coordinator,客户端直接均衡请求到集群中各节点,易于水平扩展。

三、部署与集群拓扑

3.1 单机部署

以 Linux 环境为例,快速安装与启动 Memcached:

# 安装(以 Ubuntu 为例)
sudo apt-get update
sudo apt-get install memcached

# 启动,并指定监听端口(默认 11211)与最大内存尺寸
sudo memcached -d -m 1024 -p 11211 -u memcache

# 参数说明:
# -m 1024   : 最大使用 1024MB 内存
# -p 11211  : 监听 TCP 端口为 11211
# -u memcache : 以 memcache 用户运行

启动后,可通过以下命令验证:

# 查看进程
ps aux | grep memcached

# 测试客户端连通性
echo "stats" | nc localhost 11211

3.2 集群部署(多实例)

在生产环境通常需要多台服务器运行 Memcached 实例,以分担负载。常见做法:

  1. 多结点分布式
    将 N 台 Memcached 服务器节点部署在不同机器或容器上,并通过客户端的一致性哈希算法决定将每个 key 存储到哪个节点。如下:

    • 节点列表:["10.0.0.1:11211", "10.0.0.2:11211", "10.0.0.3:11211"]
    • 客户端根据 Ketama 哈希环,将 key 映射到相应节点。
  2. 多进程多端口
    在同一台机器上,可同时运行多个 memcached 实例,分别绑定不同的端口或 IP。适用于资源隔离或多租户场景。
图1:Memcached 分布式集群架构示意图
上方示例图展示了 3 台服务器(Server1、Server2、Server3),及若干客户端(ClientA、ClientB、ClientC)通过一致性哈希或环状哈希机制将请求发送到相应节点。

四、客户端应用:常见语言示例

4.1 Python 客户端示例(使用 pymemcache

from pymemcache.client.hash import HashClient

# 假设有三个 Memcached 节点
servers = [("10.0.0.1", 11211), ("10.0.0.2", 11211), ("10.0.0.3", 11211)]
client = HashClient(servers)

# 设置数据
key = "user:1001"
value = {"name": "Bob", "age": 25}
client.set(key, str(value), expire=120)  # 将 dict 转为字符串并缓存 120 秒

# 获取数据
result = client.get(key)
if result:
    print("缓存命中,值:", result.decode())

# 删除数据
client.delete(key)

说明

  • HashClient 会自动根据 key 值做一致性哈希映射到对应节点。
  • expire 为过期时间(秒),默认为 0 表示永不过期。

4.2 Java 客户端示例(使用 spymemcached

import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;

public class MemcachedJavaExample {
    public static void main(String[] args) throws Exception {
        // 定义集群节点
        MemcachedClient client = new MemcachedClient(
            new InetSocketAddress("10.0.0.1", 11211),
            new InetSocketAddress("10.0.0.2", 11211),
            new InetSocketAddress("10.0.0.3", 11211)
        );

        // 写入缓存
        String key = "session:abcd1234";
        String value = "user=Bob;role=admin";
        client.set(key, 300, value);  // 缓存 300 秒

        // 读取缓存
        Object cached = client.get(key);
        if (cached != null) {
            System.out.println("缓存获取: " + cached.toString());
        } else {
            System.out.println("未命中");
        }

        // 删除缓存
        client.delete(key);

        client.shutdown();
    }
}

说明

  • MemcachedClient 构造时传入多个节点,会自动使用一致性哈希算法分布数据。

4.3 PHP 客户端示例(使用 Memcached 扩展)

<?php
// 初始化 Memcached 客户端
$m = new Memcached();
$m->addServer('10.0.0.1', 11211);
$m->addServer('10.0.0.2', 11211);
$m->addServer('10.0.0.3', 11211);

// 设置缓存
$m->set('page:home', file_get_contents('home.html'), 3600);

// 获取缓存
$html = $m->get('page:home');
if ($html) {
    echo "从缓存加载首页内容";
    echo $html;
} else {
    echo "缓存未命中,重新生成并设置";
    // ... 重新生成 ...
}

// 删除缓存
$m->delete('page:home');
?>

说明

  • PHP 内置 Memcached 扩展支持一致性哈希,addServer() 多次调用即可添加多个节点。

五、一致性哈希与扩缩容策略

5.1 一致性哈希原理

  • 传统哈希(如 hash(key) % N)在节点上下线或扩容时会导致大量 key 重新映射,缓存命中率骤降。
  • 一致性哈希(Consistent Hashing) 将整个哈希空间想象成一个环(0\~2³²-1),每个服务器(包括虚拟节点)在环上占据一个或多个位置。Key 通过相同哈希映射到环上的某个点,然后顺时针找到第一个服务器节点来存储。
  • 当某台服务器加入或离开,只会影响其相邻区域的少量 key,不会造成全局大量失效。

5.2 虚拟节点(Virtual Node)

  • 为了避免服务器节点分布不均,一般会为每台真实服务器创建多个虚拟节点(例如 100\~200 个),将它们做哈希后分布到环上。
  • 客户端在环上找到的第一个虚拟节点对应一个真实服务器,即可减少节点数量变化带来的数据迁移。

5.3 扩容与缩容示例

  1. 添加服务器

    • 新服务器加入后,客户端会在一致性哈希环上插入对应的虚拟节点,环上受影响的 key 只需迁移给新服务器。
    • 示例流程(概念):

      1. 在环上计算新服务器的每个虚拟节点位置。
      2. 客户端更新哈希环映射表。
      3. 新服务器接管部分 key(旧服务器负责将这些 key 迁移到新服务器)。
  2. 删除服务器

    • 移除服务器对应的虚拟节点,环上相邻节点接管其负责的 key。
    • 只需将原本属于该服务器的 key 重新写入相邻节点,其他 key 不受影响。

六、缓存失效与淘汰策略

6.1 过期(TTL)与显式删除

  • 当通过 set 命令设置 expire 参数时,Memcached 会在后台检查并自动清理已过期的数据。
  • 客户端也可以显式调用 delete key 删除某个缓存项。

6.2 LRU 淘汰机制

  • Memcached 在单实例内部使用LRU (Least Recently Used) 策略管理各 slab class 中存储的对象:当某个 slab class 内存空间用尽,且无法分配新对象时,会淘汰该 slab class 中最久未被访问的 key。
  • 各 slab class 独立维护 LRU 列表,避免不同大小对象相互挤占空间。

6.3 高阶淘汰策略:LRU / LFU / 带样本的 LRU

  • 虽然 Memcached 默认仅支持 LRU,但可以结合外部模块或客户端策略实现如 LFU (Least Frequently Used) 等更复杂的淘汰算法。
  • 例如:将部分热点 key 在客户端层面持续刷新过期时间,使得热点 key 不被淘汰。

七、性能优化与运维注意事项

7.1 配置调优

  1. 内存与 slab 配置

    • 通过 -m 参数设置合适的内存总量。
    • 使用 stats itemsstats slabs 命令监控各 slab class 的命中率与被淘汰次数,根据实际情况调整 slab 分配。
  2. 网络参数

    • 对高并发场景,应调整系统 ulimit -n 打开文件描述符数。
    • 根据网络带宽计算最大并发客户端连接数,避免出现 TCP 队头阻塞问题。
  3. 多核优化

    • Memcached 默认使用多线程架构,可通过 -t 参数指定线程数,例如 memcached -m 2048 -p 11211 -t 4。线程数可设置为 CPU 核心数或更高,但要注意锁竞争。

7.2 监控与告警

  • 关键指标

    • Cache Hit Ratio: get_hits / get_misses,命中率过低时需检查 key 设计或容量是否不足;
    • Evictions(被淘汰次数):若快速递增,说明 memory 不足或某些 slab class 项过大;
    • Connection Stats: curr_connections, total_connections
    • Bytes Read/Written, cmd_get, cmd_set:表示负载情况。
  • 推荐通过 Prometheus + Grafana 或 InfluxDB + Grafana 监控 Memcached 指标,并设置阈值告警,如命中率低于 80% 或被淘汰次数猛增时触发报警。

7.3 数据一致性与回源策略

  • 缓存穿透:若缓存不存在时直接到后端 DB 查询,可能造成高并发下产生大量 DB 访问(击穿)。

    • 解决方案:

      • 在缓存中写入空对象或 Bloom Filter 检测,避免不存在 key 大量打到 DB。
  • 缓存雪崩:多条缓存同时过期,导致瞬间大量请求到后端。

    • 解决方案:

      • 使用随机过期时间(TTL 增减少量随机值);
      • 在热点数据点使用永不过期 + 定时更新策略。
  • 数据不一致:当后端数据更新,未及时更新或删除缓存,导致脏数据。

    • 解决方案:

      • 双写策略:更新数据库的同时清除或更新缓存;
      • 异步 Cache Invalidation:使用消息队列通知其他节点清除缓存。

八、总结

Memcached 作为一款成熟、简洁的分布式内存缓存系统,具有低延迟、高吞吐、易扩展等特点。通过合理的部署、客户端一致性哈希、有效的淘汰策略和运维监控,可以显著提升应用性能,减轻后端数据库压力。

  • 核心优势:秒级响应、极低延迟、横向扩展简单。
  • 适用场景:Session 缓存、热点数据缓存、页面缓存、API 响应缓存等。
  • 注意事项:需要设计合理的 key 规范、过期策略和缓存更新机制,以防止缓存击穿/雪崩/污染。
最后修改于:2025年06月02日 18:04

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日