Article
第一章:Memcached 概述
1.1 什么是 Memcached
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Memcached | 一个开源的高性能分布式内存对象缓存系统,用于通过在内存中缓存数据和对象来减少数据库负载,提升动态 Web 应用速度。 | 不支持持久化,重启后数据丢失。 |
| 键值存储(Key-Value Store) | Memcached 以简单的 key-value 形式存储数据,key 为字符串,value 为任意二进制数据(最大 1MB)。 | key 长度限制通常为 250 字节。 |
| 分布式架构 | Memcached 本身无内置集群协议,依赖客户端实现分布式(如一致性哈希)。多个 Memcached 实例独立运行,互不通信。 | 服务端无主从、无复制机制。 |
| 协议基础 | 基于文本协议(也可使用二进制协议),可通过 telnet 或 netcat 直接交互。 | 默认监听 TCP 11211 端口。 |
1.2 Memcached 的特点与优势
| 特点/优势名称 | 说明 | 注意事项 |
|---|---|---|
| 高性能 | 纯内存操作,C 语言编写,单线程事件驱动模型(早期版本),延迟极低。 | 多核利用率依赖多实例部署。 |
| 简单轻量 | API 极简,仅支持基本的 set/get/delete/incr/decr 等操作,无复杂数据结构。 | 不适合需要列表、集合等结构的场景。 |
| 分布式友好 | 客户端可轻松实现跨多节点的数据分片,扩展性好。 | 需自行处理节点增减时的数据迁移。 |
| 内存高效管理 | 使用 Slab Allocator 机制分配内存,避免内存碎片。 | 预分配 slab class,可能造成内部碎片。 |
| 广泛语言支持 | 几乎所有主流编程语言均有成熟客户端库(Python、Java、PHP、Node.js 等)。 | 各客户端功能和稳定性略有差异。 |
| 无认证与加密 | 默认无身份验证和传输加密机制,需部署在可信内网。 | 公网暴露存在安全风险。 |
1.3 Memcached 与其他缓存系统对比(如 Redis)
| 对比维度 | Memcached | Redis | 注意事项 |
|---|---|---|---|
| 数据结构支持 | 仅支持字符串(二进制安全) | 支持字符串、哈希、列表、集合、有序集合、位图、流等 | Redis 功能更丰富,适合复杂场景。 |
| 持久化 | 不支持 | 支持 RDB 快照和 AOF 日志两种持久化方式 | Memcached 仅用于临时缓存。 |
| 内存管理 | Slab Allocator,固定大小块分配 | 简单动态分配(jemalloc),更灵活 | Memcached 可能浪费内存(slab 内部碎片)。 |
| 多线程 | 传统版本为单线程;新版(1.6+)支持多线程 I/O | Redis 6.0+ 引入 I/O 多线程,核心命令仍单线程执行 | 高并发下新版 Memcached 性能更优。 |
| 集群能力 | 无内置集群,依赖客户端分片 | 支持 Redis Cluster(自动分片、故障转移) | Redis 集群运维更自动化。 |
| 事务与 Lua 脚本 | 不支持 | 支持 MULTI/EXEC 事务及 Lua 脚本 | Redis 可实现原子复合操作。 |
| 发布/订阅 | 不支持 | 支持 Pub/Sub 模式 | Redis 可用于消息传递场景。 |
| 最大值大小 | 默认 1MB(可调,但不推荐超过) | 默认 512MB(可配置) | Memcached 不适合大对象缓存。 |
| 社区与生态 | 成熟稳定,但近年发展缓慢 | 活跃社区,云厂商广泛支持,工具链丰富 | 新项目更倾向选择 Redis。 |
第二章:Memcached 安装与配置
2.1 在 Linux 系统中安装 Memcached
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 更新系统包 | 执行 sudo apt update(Debian/Ubuntu)或 sudo yum update(CentOS/RHEL) | 确保系统为最新状态,避免依赖冲突。 |
| 安装 Memcached | Ubuntu/Debian: sudo apt install memcachedCentOS/RHEL: sudo yum install memcached | 部分旧版本需启用 EPEL 仓库(如 CentOS 7)。 |
| 安装 libmemcached(可选) | sudo apt install libmemcached-tools 或 sudo yum install libmemcached | 提供额外工具如 memstat、memcp 等。 |
| 验证安装 | 执行 memcached -h 查看帮助信息,确认版本号 | 若命令未找到,检查 PATH 或重新安装。 |
| 设置开机自启 | sudo systemctl enable memcached | 默认配置文件通常位于 /etc/memcached.conf。 |
2.2 在 Windows 系统中安装 Memcached
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 下载 Windows 版本 | 从可信第三方源(如 https://github.com/memcached/memcached/wiki/Install)下载预编译的 .exe 文件(如 memcached-win64.zip) | 官方不提供 Windows 支持,需使用社区维护版本。 |
| 解压到目标目录 | 例如解压到 C:\memcached | 路径不要包含空格或中文字符。 |
| 注册为系统服务 | 以管理员身份运行 CMD,执行:C:\memcached\memcached.exe -d install | 需要管理员权限,否则注册失败。 |
| 启动服务 | 执行 net start memcached 或通过”服务”管理界面启动 | 默认监听 11211 端口。 |
| 验证运行状态 | 执行 telnet localhost 11211,若连接成功则服务正常 | 若 telnet 未启用,需在”启用或关闭 Windows 功能”中开启。 |
| 卸载服务(可选) | 执行 C:\memcached\memcached.exe -d uninstall | 卸载前需先停止服务。 |
2.3 启动与停止 Memcached 服务
| 操作名称 | 操作细节 | 注意事项 |
|---|---|---|
| 手动前台启动 | 直接运行 memcached -p 11211 -m 64 -u memcached(Linux) | 适合调试,关闭终端即终止进程。 |
| 后台启动(Linux) | 使用 systemd:sudo systemctl start memcached或使用 -d 参数: memcached -d -m 64 | -d 表示 daemon 模式(部分版本支持)。 |
| 停止服务(Linux) | sudo systemctl stop memcached 或 killall memcached | 避免直接 kill -9,可能导致资源未释放。 |
| 启动服务(Windows) | net start memcached | 仅适用于已注册为服务的情况。 |
| 停止服务(Windows) | net stop memcached | 停止后所有缓存数据立即丢失。 |
| 检查端口监听 | ss -tuln | grep 11211(Linux)或 netstat -ano | findstr 11211(Windows) | 确认服务是否真正监听。 |
2.4 常用启动参数详解
| 参数名称 | 语法示例 | 用途说明 | 注意事项 |
|---|---|---|---|
| -p | -p 11211 | 指定监听的 TCP 端口,默认为 11211 | 需确保端口未被占用。 |
| -U | -U 11211 | 指定监听的 UDP 端口(可设为 0 禁用 UDP) | UDP 不可靠,生产环境建议禁用(-U 0)。 |
| -l | -l 127.0.0.1 | 指定监听的 IP 地址,限制访问来源 | 默认监听所有接口(0.0.0.0),存在安全风险。 |
| -m | -m 512 | 分配给 Memcached 的最大内存(MB),默认 64MB | 实际使用可能略超,因 slab 元数据开销。 |
| -c | -c 1024 | 最大并发连接数,默认 1024 | 连接数过高可能耗尽文件描述符。 |
| -t | -t 4 | 使用的线程数(Memcached 1.6+ 支持多线程 I/O) | 旧版本忽略此参数。 |
| -d | -d | 以守护进程(daemon)方式运行 | Windows 版使用 -d install 注册服务。 |
| -u | -u nobody | 指定运行用户(Linux 下需 root 启动后切换) | 不能以 root 用户运行(安全限制)。 |
| -f | -f 1.25 | slab 增长因子(chunk size 每级增长倍数),默认 1.25 | 调整影响内存利用率和碎片率。 |
| -n | -n 48 | 单个 item 最小空间(字节),包含 key/value/header 开销 | 默认 48 字节,过小会浪费内存。 |
| -M | -M | 内存耗尽时返回错误(而非驱逐旧数据) | 默认行为是 LRU 淘汰,慎用 -M。 |
| -o | -o modern 或 -o lru_crawler | 启用高级选项(如 LRU crawler、slab automove 等) | 需 Memcached 1.5+ 支持。 |
| -v / -vv / -vvv | -vv | 输出详细日志(-v: 普通, -vv: 详细, -vvv: 最详细) | 调试用,生产环境避免高 verbose 级别。 |
第三章:Memcached 命令行操作基础
3.1 连接 Memcached 服务器(telnet/nc)
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 使用 telnet 连接 | 在终端执行:telnet 127.0.0.1 11211 | 若提示”command not found”,需安装 telnet 客户端(如 sudo apt install telnet)。 |
| 使用 nc(netcat)连接 | 执行:nc 127.0.0.1 11211 | nc 更轻量,适合脚本自动化;部分系统需安装 netcat 或 ncat。 |
| 验证连接成功 | 成功连接后无提示符,可直接输入 Memcached 命令 | 若连接被拒绝,检查 Memcached 是否运行及监听地址/端口。 |
| 退出连接 | 输入 quit 并回车,或按 Ctrl+] 后输入 quit(telnet) | 直接关闭终端可能导致连接未正常释放。 |
| 连接远程服务器 | 将 127.0.0.1 替换为远程 IP,如 telnet 192.168.1.100 11211 | 确保防火墙开放 11211 端口,且 Memcached 监听非 127.0.0.1 地址。 |
3.2 存储类命令(set/add/replace/prepend/append)
| 方法名称 | 语法 | 用途说明 | 代码示例(在 telnet/nc 中输入) | 注意事项 |
|---|---|---|---|---|
| set | set <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 无条件存储 key-value,存在则覆盖 | set username 0 0 5alice | flags 通常设为 0;exptime=0 表示永不过期(实际受内存淘汰影响)。 |
| add | add <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 仅当 key 不存在时存储 | add token 0 3600 10abc123def | 若 key 已存在,返回 NOT_STORED。 |
| replace | replace <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 仅当 key 存在时替换其值 | replace username 0 0 6bob | 若 key 不存在,返回 NOT_STORED。 |
| append | append <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 在现有 value 后追加数据(二进制安全) | append username 0 0 1e | key 必须存在,否则失败;flags 和 exptime 被忽略(但必须提供)。 |
| prepend | prepend <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 在现有 value 前插入数据 | prepend username 0 0 1A | 同 append,flags/exptime 仅占位,不生效。 |
注:所有命令末尾需以
\r\n(回车换行)结束,但在 telnet/nc 交互中直接按 Enter 即可。[noreply]为可选参数,若指定则服务器不返回 STORED/NOT_STORED。
3.3 获取类命令(get/gets)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| get | get <key> | 获取指定 key 的 value | get username | 可一次获取多个 key:get k1 k2 k3 |
| gets | gets <key> | 获取 key 的 value 及其 CAS 唯一标识符 | gets username | 用于实现 check-and-set 乐观锁机制 |
返回格式示例(get):
VALUE username 0 5
alice
END
返回格式示例(gets):
VALUE username 0 5 12345
alice
END
其中 12345 为 CAS token。
3.4 删除类命令(delete)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| delete | delete <key> [noreply] | 删除指定 key | delete username | 若 key 不存在,返回 NOT_FOUND;添加 noreply 可抑制响应。 |
成功删除返回 DELETED,失败返回 NOT_FOUND。
3.5 计数类命令(incr/decr)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| incr | incr <key> <value> [noreply] | 对 key 对应的数值原子递增 | incr counter 5 | key 必须已存在且为纯数字字符串(0-9),否则返回 ERROR。 |
| decr | decr <key> <value> [noreply] | 对 key 对应的数值原子递减(最小为 0) | decr counter 2 | 递减不会低于 0;不能用于负数。 |
示例流程:
set counter 0 0 1
3
incr counter 2
→ 5
decr counter 10
→ 0
3.6 其他实用命令(stats/flush_all/version/quit)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| stats | stats | 获取服务器统计信息(连接数、命中率、内存等) | stats | 可加子命令如 stats items、stats slabs 查看详细信息。 |
| flush_all | flush_all [delay] [noreply] | 立即或延迟清空所有缓存数据 | flush_all 10 | delay 单位为秒;数据立即不可见,但内存回收依赖 LRU。 |
| version | version | 返回 Memcached 版本号 | version | 返回如 VERSION 1.6.12。 |
| quit | quit | 关闭当前连接 | quit | 客户端应主动调用以释放资源。 |
stats 常见输出字段:
get_hits/get_misses:用于计算命中率curr_items:当前缓存项数量bytes:当前使用内存量(字节)total_connections:历史总连接数
第四章:Memcached 数据管理机制
4.1 内存分配机制(Slab Allocator)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Slab | Memcached 将内存划分为多个 Slab Class,每个 Slab 是 1MB 的连续内存块。 | 启动时预分配,总内存由 -m 参数控制。 |
| Slab Class | 按数据大小分组,每个 Class 对应一个固定的 chunk size(如 96B、120B…)。 | chunk size = previous_size × growth_factor(默认 1.25)。 |
| Chunk | Slab 被进一步划分为等长的 Chunk,用于存储单个 item(key + value + 元数据)。 | 一个 item 只能放入 chunk size ≥ item 总大小的 Slab Class。 |
| Item 结构 | 包含 key(字符串)、value(二进制)、flags(整数)、exptime(过期时间)、CAS token 等元数据。 | 实际占用空间 = key_len + value_len + 固定头部(约 32–48 字节)。 |
| 内存碎片(内部) | 若 item 大小小于 chunk size,剩余空间浪费,称为”内部碎片”。 | 小对象缓存效率高,大对象易造成浪费。 |
| Slab Reassignment(可选) | 新版 Memcached 支持 -o slab_reassign 动态迁移空闲 Slab 到更需 Class。 | 需显式启用,避免某些 Class 内存耗尽而其他闲置。 |
4.2 LRU 淘汰策略
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| LRU(Least Recently Used) | 当内存满且需插入新 item 时,优先淘汰最久未被访问的 item。 | 默认全局 LRU,新版支持 per-slab-class LRU。 |
| 淘汰触发条件 | 所有 Slab Class 的可用 chunk 耗尽,且无法分配新 chunk。 | 即使总内存未满,若某 Class 满而 item 属于该 Class,仍会淘汰。 |
| evictions 统计 | stats 命令中的 evictions 字段记录因内存不足被驱逐的 item 数量。 | 高 evictions 表示缓存容量不足或热点不均。 |
| LRU Crawler | 后台线程定期扫描 Slab,清理已过期但未被访问的 item(需 -o lru_crawler 启用)。 | 减少内存浪费,提升命中率。 |
| 不淘汰的情况 | 若启动时加 -M 参数,则内存满时直接返回错误,不执行 LRU 淘汰。 | 适用于不允许缓存污染的关键场景。 |
4.3 键的命名规范与限制
| 限制项 | 说明 | 注意事项 |
|---|---|---|
| 最大长度 | key 最大 250 字节(部分客户端限制为 255,但服务端强制截断至 250)。 | 超长 key 会被拒绝或静默截断,导致数据错乱。 |
| 字符集 | 支持任意二进制安全字符串(包括 null 字节),但建议使用 ASCII 或 UTF-8。 | 避免控制字符(如换行、回车),可能干扰文本协议解析。 |
| 命名建议 | 使用有意义的前缀(如 user:123:profile),便于管理和调试。 | 避免纯数字 key(某些客户端可能混淆类型)。 |
| 唯一性 | key 在整个 Memcached 实例中必须唯一,相同 key 会覆盖旧值。 | 分布式环境下,key 哈希到哪个节点由客户端决定。 |
| 特殊字符处理 | 空格、冒号、下划线等均可使用,但避免使用 \r\n(协议分隔符)。 | 推荐使用字母、数字、连字符、下划线组合。 |
4.4 值的大小与过期时间限制
| 限制项 | 说明 | 注意事项 |
|---|---|---|
| 最大值大小 | 单个 value 最大为 1MB(1,048,576 字节),包含 key 和元数据总和 ≤ 1MB。 | 超限将返回 SERVER_ERROR object too large for cache。 |
| 实际可用 value 空间 | ≈ 1MB - key_len - 头部开销(通常 ≥ 48 字节) | 存储 1MB value 时,key 应尽量短。 |
| 过期时间(exptime) | 支持两种格式: • 相对秒数(如 3600 表示 1 小时后过期) • Unix 时间戳(≥ 30 天秒数,如 1735689600) | 若设为 0,表示”永不过期”(但仍可能被 LRU 淘汰)。 |
| 最大过期时间 | 相对时间最大支持约 30 天(2,592,000 秒);超过则视为时间戳。 | 误将小时间戳(如 1000)当作相对时间会导致立即过期。 |
| 过期行为 | 过期 item 不会立即删除,仅在 get 访问或 LRU Crawler 扫描时清理。 | 内存不会实时释放,可能短暂占用资源。 |
| 二进制安全 | value 可为任意二进制数据(图片、序列化对象等),无字符集限制。 | 客户端需自行处理序列化/反序列化。 |
第五章:Memcached 客户端使用(语言集成)
5.1 Python 客户端(python-memcached / pymemcache)
| 方法/组件名称 | 语法 / 安装方式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 安装 python-memcached | pip install python-memcached | 老牌客户端,基于文本协议 | from memcache import Clientmc = Client(['127.0.0.1:11211']) | 已停止维护,不支持二进制协议,线程不安全。 |
| 安装 pymemcache | pip install pymemcache | 现代客户端,支持文本/二进制协议,性能更好 | from pymemcache.client import basemc = base.Client(('localhost', 11211)) | 推荐新项目使用;支持连接池、哈希环等。 |
| set | client.set(key, value, time=0, min_compress_len=0) | 存储键值对 | mc.set('user:1001', {'name': 'Alice'}, time=3600) | time 为过期时间(秒);value 需可序列化(如 str、bytes、dict)。 |
| get | client.get(key) | 获取单个值 | data = mc.get('user:1001') | 未命中返回 None。 |
| gets | client.gets(key) | 获取值和 CAS token | value, cas = mc.gets('counter') | 用于配合 cas 实现乐观锁。 |
| cas | client.cas(key, value, cas, time=0) | 仅当 CAS 匹配时更新 | mc.cas('counter', 42, cas_token, time=0) | 返回 True/False 表示是否成功。 |
| add / replace | client.add(key, value, time=0)client.replace(key, value, time=0) | 条件写入 | mc.add('lock', 'held', time=10) | add 仅在 key 不存在时成功;replace 仅在存在时成功。 |
| delete | client.delete(key, noreply=False) | 删除键 | mc.delete('temp_data') | noreply=True 可提升性能但无确认。 |
| incr / decr | client.incr(key, delta=1)client.decr(key, delta=1) | 原子计数 | mc.incr('page_views', 1) | key 必须已存在且为数字字符串。 |
| 多节点支持 | from pymemcache.client.hash import HashClientclient = HashClient([('h1',11211), ('h2',11211)]) | 一致性哈希分片 | client.set('user:1002', data) | 自动路由到对应节点。 |
5.2 Java 客户端(SpyMemcached / XMemcached)
| 方法/组件名称 | 语法 / 安装方式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| SpyMemcached(Maven) | <dependency><groupId>net.spy</groupId><artifactId>spymemcached</artifactId><version>2.12.3</version></dependency> | 异步、高性能,但已归档 | MemcachedClient mc = new MemcachedClient(new InetSocketAddress("localhost", 11211)); | 不再积极维护,建议迁移到其他方案。 |
| XMemcached(Maven) | <dependency><groupId>com.googlecode.xmemcached</groupId><artifactId>xmemcached</artifactId><version>2.4.8</version></dependency> | 支持连接池、高并发、协议灵活 | XMemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:11211"));XMemcachedClient mc = builder.build(); | 推荐生产使用;支持权重、故障转移。 |
| set | mc.set(String key, int exp, Object value) | 存储 | mc.set("session:abc", 1800, sessionData); | exp=0 表示永不过期;value 需实现 Serializable。 |
| get | Object mc.get(String key) | 获取 | Session s = (Session) mc.get("session:abc"); | 未命中返回 null。 |
| gets | GetsResponse<Object> mc.gets(String key) | 获取带 CAS | GetsResponse r = mc.gets("counter"); | 通过 r.getCas() 获取 token。 |
| cas | boolean mc.cas(String key, long casId, int exp, Object value) | CAS 更新 | boolean ok = mc.cas("counter", casId, 0, newValue); | 返回是否成功。 |
| delete | boolean mc.delete(String key) | 删除 | mc.delete("temp_key"); | 返回是否删除成功。 |
| incr / decr | long mc.incr(String key, long by)long mc.decr(String key, long by) | 计数 | long views = mc.incr("views", 1); | 若 key 不存在,incr 可能失败(部分版本支持初始化)。 |
| 异步操作 | Future<Boolean> future = mc.set("key", 0, "val"); | 非阻塞调用 | Boolean result = future.get(); | SpyMemcached 默认异步;XMemcached 可选。 |
5.3 Node.js 客户端(memjs / memcached)
| 方法/组件名称 | 语法 / 安装方式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 安装 memjs | npm install memjs | 简洁、支持二进制协议 | const memjs = require('memjs');const client = memjs.Client.create(); | 默认连接 localhost:11211;支持多节点。 |
| 安装 memcached | npm install memcached | 功能丰富,支持连接池、哈希 | const Memcached = require('memcached');const client = new Memcached('localhost:11211'); | 更成熟,但配置复杂。 |
| set | client.set(key, value, ttl, callback) | 存储 | client.set('user:1', JSON.stringify(user), 3600, (err) => { ... }); | ttl=0 表示永不过期;value 通常为字符串或 Buffer。 |
| get | client.get(key, callback) | 获取 | client.get('user:1', (err, data) => { console.log(data); }); | 未命中时 data 为 false。 |
| gets | client.gets(key, callback) | 获取带 CAS | client.gets('counter', (err, data, cas) => { ... }); | memjs 支持;memcached 模块需查文档。 |
| cas | client.cas(key, value, cas, ttl, callback) | CAS 更新 | client.cas('counter', '42', casToken, 0, (err, success) => { ... }); | 仅 memjs 明确支持。 |
| del | client.del(key, callback) | 删除 | client.del('old_key', (err) => { ... }); | 别名为 delete,但部分模块用 del 避免关键字冲突。 |
| incr / decr | client.incr(key, amount, callback)client.decr(key, amount, callback) | 计数 | client.incr('hits', 1, (err, newCount) => { ... }); | amount 必须为正整数。 |
| 多节点配置 | memjs.Client.create('host1:11211,host2:11211') | 分布式缓存 | const client = memjs.Client.create('192.168.1.10:11211,192.168.1.11:11211'); | 自动使用一致性哈希。 |
5.4 PHP 客户端(Memcached 扩展)
| 方法/组件名称 | 语法 / 安装方式 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 安装扩展 | sudo apt install php-memcached(Linux)或通过 PECL 安装 | 高性能 C 扩展,支持二进制协议 | $mc = new Memcached();$mc->addServer('localhost', 11211); | 需启用 memcached 扩展(非 memcache)。 |
| set | $mc->set(string $key, mixed $value, int $expiration = 0) | 存储 | $mc->set('token', 'abc123', 3600); | $value 可为任意可序列化类型(自动 serialize)。 |
| get | $mc->get(string $key) | 获取 | $data = $mc->get('token'); | 未命中返回 false。 |
| getMulti | $mc->getMulti(array $keys) | 批量获取 | $results = $mc->getMulti(['k1', 'k2']); | 返回关联数组。 |
| add / replace | $mc->add($key, $value, $exp)$mc->replace($key, $value, $exp) | 条件写入 | $mc->add('lock', true, 10); | 成功返回 true,否则 false。 |
| delete | $mc->delete(string $key, int $time = 0) | 删除 | $mc->delete('temp'); | $time > 0 表示延迟删除(不常用)。 |
| increment / decrement | $mc->increment($key, $offset = 1)$mc->decrement($key, $offset = 1) | 计数 | $mc->increment('counter', 5); | 若 key 不存在,increment 可能初始化为 offset(依赖配置)。 |
| getCasToken | $mc->get($key, null, Memcached::GET_EXTENDED) | 获取 CAS token | $result = $mc->get('counter', null, Memcached::GET_EXTENDED);$cas = $result['cas']; | 需配合 cas() 使用。 |
| cas | $mc->cas(float $cas_token, string $key, mixed $value, int $exp = 0) | CAS 更新 | $mc->cas($cas, 'counter', 100); | 返回 bool 表示是否成功。 |
| 多服务器 | $mc->addServers([['host1',11211], ['host2',11211]]); | 分布式部署 | $mc->addServers([['192.168.1.10',11211], ['192.168.1.11',11211]]); | 自动使用一致性哈希。 |
第六章:Memcached 高可用与集群
6.1 客户端一致性哈希(Consistent Hashing)
| 概念/操作名称 | 说明 | 注意事项 |
|---|---|---|
| 一致性哈希目的 | 在多节点 Memcached 集群中,当节点增减时,最小化 key 重映射比例(理想为 1/N)。 | 传统取模哈希在节点变化时会导致几乎所有 key 重新分配。 |
| 虚拟节点(Virtual Node) | 每个物理节点映射多个虚拟节点(如 100~200 个),均匀分布在哈希环上。 | 提高负载均衡性,避免数据倾斜。 |
| 哈希环(Hash Ring) | 将 0 ~ 2³²-1 的整数空间首尾相连形成环,key 和节点均通过哈希函数映射到环上。 | 常用哈希函数:MD5、CRC32、MurmurHash。 |
| key 路由规则 | 对 key 哈希后,顺时针查找第一个虚拟节点,其所属物理节点即为目标服务器。 | 所有客户端必须使用相同哈希算法和虚拟节点策略。 |
| 客户端实现示例 | Python pymemcache.HashClient、PHP Memcached::addServers、Java XMemcached | 不同语言客户端的虚拟节点数量和哈希算法可能不同,跨语言需统一。 |
| 优势 | 节点扩容/缩容时,仅影响相邻虚拟节点的数据,缓存命中率波动小。 | 无法自动复制数据,故障节点数据完全丢失。 |
| 局限性 | 不提供高可用(无副本),单点故障导致部分缓存失效。 | 需配合应用层重试或降级策略。 |
6.2 多节点部署与故障转移
| 操作/机制名称 | 操作细节 | 注意事项 |
|---|---|---|
| 多节点独立部署 | 在多台服务器分别启动 Memcached 实例,监听各自 IP:11211 | 各实例无通信,完全独立;总容量为各节点内存之和。 |
| 客户端配置多地址 | 在客户端初始化时传入所有节点地址列表(如 ['node1:11211', 'node2:11211']) | 必须启用一致性哈希(而非随机或轮询)。 |
| 节点故障表现 | 故障节点上的 key 无法访问,客户端 get 返回 miss,触发回源数据库 | 缓存雪崩风险:大量请求穿透到后端。 |
| 故障检测 | 客户端通常无主动健康检查;依赖 TCP 连接失败或超时判断节点不可用 | 可通过连接池或自定义 wrapper 实现简单探活。 |
| 自动剔除故障节点 | 部分高级客户端(如 XMemcached)支持权重置零或临时移除故障节点 | 需谨慎处理,避免误判导致容量骤降。 |
| 数据恢复策略 | 无自动恢复;依赖应用层在 get miss 时从数据库加载并重新 set 到新节点 | 热点 key 可能造成数据库瞬时压力。 |
| 扩容操作步骤 | 1. 新增 Memcached 实例 2. 客户端更新节点列表 3. 重启或热加载配置 | 扩容后仅新写入的 key 分布到新节点,旧数据仍留在原节点。 |
| 缩容操作步骤 | 1. 从客户端移除节点 2. 停止该 Memcached 实例 | 缩容节点上的数据立即不可用,需接受短暂命中率下降。 |
6.3 与代理方案结合(如 mcrouter)
| 组件/操作名称 | 说明 | 注意事项 |
|---|---|---|
| mcrouter 简介 | Facebook 开源的 Memcached 协议代理,支持路由、复制、故障转移、多集群等高级功能。 | 用 C++ 编写,高性能,支持百万级 QPS。 |
| 核心功能 | • Prefix 路由(如 prod.* → 集群 A)• 同步/异步复制 • 自动故障转移 • 请求日志与统计 | 配置灵活,适合大规模生产环境。 |
| 部署架构 | 应用 → mcrouter(本地或独立集群) → Memcached 节点组 | mcrouter 本身无状态,可水平扩展。 |
| 安装方式 | 官方提供 RPM/DEB 包或源码编译(依赖 folly、wangle 等) | 推荐使用官方 Docker 镜像快速部署。 |
| 基础配置示例 | `{ “pools”: { “A”: { “servers”: [“10.0.1.10:11211”, “10.0.1.11:11211”] } }, “route”: “PoolRoute | A” }` |
| 复制模式(Replicated) | 写操作同步发送到多个池,读操作从主池获取 | 提升可用性,但增加写延迟和带宽消耗。 |
| Miss Error Routing | 当主集群 miss 时,自动 fallback 到备份集群 | 用于多级缓存或异地容灾。 |
| 客户端连接方式 | 应用连接 mcrouter(默认端口 11211),如同连接普通 Memcached | 无需修改应用代码,仅改连接地址。 |
| 监控与运维 | 支持 stats 命令、JSON API、Prometheus exporter | 可集成到 Grafana 实现可视化监控。 |
| 替代方案 | • twemproxy(nutcracker):较老,功能较少 • AWS ElastiCache(托管服务) | mcrouter 功能最全面,但运维复杂度高。 |
第七章:Memcached 性能调优与监控
7.1 性能指标解读(stats 命令输出)
| 指标名称 | 说明 | 注意事项 |
|---|---|---|
| pid | Memcached 进程 ID | 用于进程管理。 |
| uptime | 服务已运行秒数 | 判断是否近期重启。 |
| time | 当前 Unix 时间戳 | 用于日志对齐。 |
| version | Memcached 版本号 | 确认是否为支持新特性的版本(如多线程)。 |
| curr_connections | 当前活跃连接数 | 突增可能表示连接泄漏或 DDoS。 |
| total_connections | 历史总连接数 | 结合 uptime 可估算平均连接速率。 |
| connection_structures | 当前分配的连接结构体数量 | 通常等于 curr_connections。 |
| cmd_get / cmd_set | get / set 命令总次数 | 用于计算读写比例。 |
| get_hits / get_misses | get 命中 / 未命中次数 | 缓存命中率 = get_hits / (get_hits + get_misses);理想 > 90%。 |
| evictions | 因内存不足被 LRU 淘汰的 item 数量 | 高值表示内存不足或热点不均;应结合 curr_items 分析。 |
| bytes | 当前缓存数据占用的总字节数 | 接近 -m 设置值时需扩容。 |
| curr_items | 当前缓存中的 item 总数 | 与 bytes 共同反映数据密度。 |
| total_items | 历史存储过的 item 总数 | 用于估算写入吞吐。 |
| bytes_read / bytes_written | 网络收发总字节数 | 监控带宽使用。 |
| limit_maxbytes | 内存上限(字节),由 -m 参数决定 | 例如 -m 512 → limit_maxbytes = 536870912。 |
| threads | 工作线程数(Memcached 1.6+) | 应 ≤ CPU 核心数。 |
| rusage_user / rusage_system | 进程累计 CPU 用户态/内核态时间(秒) | 用于性能分析。 |
| auth_cmds / auth_errors | 认证命令数 / 失败数(若启用 SASL) | 默认未启用,值为 0。 |
关键健康指标判断:
- 高 evictions + 低命中率 → 内存不足,需扩容或优化 key 生命周期。
- curr_connections 接近 maxconns(默认 1024) → 需调大
-c参数。 - bytes ≈ limit_maxbytes 且 evictions 持续增长 → 缓存容量已达瓶颈。
7.2 内存碎片与 slab 调优
| 调优项 | 说明 | 注意事项 |
|---|---|---|
| 查看 slab 信息 | 执行 stats slabs 获取每个 slab class 的 chunk size、chunks per page、used chunks 等 | 关注 mem_requested(实际数据大小)与 total_malloced(分配内存)之比。 |
| 查看 items 信息 | 执行 stats items 查看各 slab class 中 item 数量、age、LRU 信息 | number_hot/number_warm/number_cold 反映访问热度(需启用 LRU maintainer)。 |
| 调整增长因子(-f) | 启动参数 -f 1.2(默认 1.25)控制 slab class 间 chunk size 增长倍数 | 减小 f → 更多 class,减少内部碎片,但增加元数据开销;增大 f → 相反。 |
| 调整最小 item 大小(-n) | -n 64(默认 48)设置最小 chunk size(含头部) | 若大量存储小对象(<48B),适当减小可节省内存。 |
| 启用 slab_automove | 启动参数 -o slab_automove=1 自动迁移空闲 slab 到繁忙 class | 需 Memcached ≥ 1.5;缓解某些 class 内存耗尽问题。 |
| 启用 slab_reassign | -o slab_reassign 允许后台线程回收完全空闲的 slab 并重分配 | 与 slab_automove 配合使用效果更佳。 |
| 监控内部碎片率 | 内部碎片率 ≈ 1 - (mem_requested / total_malloced) | >20% 表示碎片严重,需调整 -f 或 -n。 |
| 预分配大对象专用 class | 通过 -o use_memdup 或客户端预分片,避免大对象挤占小对象内存 | 不推荐存储接近 1MB 的对象。 |
| 避免频繁 set 大小变化的 key | 同一 key 反复 set 不同大小 value 会导致跨 slab 移动,产生垃圾 | 尽量保持 value 大小稳定。 |
典型调优场景:若
stats slabs显示 class 1(chunk_size=96)的total_malloced=100MB 但mem_requested=60MB,则内部碎片率达 40%,可尝试-f 1.1增加 class 密度。
7.3 监控工具(如 memcached-top、Prometheus + Exporter)
| 工具名称 | 安装/使用方式 | 用途说明 | 注意事项 |
|---|---|---|---|
| memcached-top | pip install memcached-top运行: memcached-top --host=127.0.0.1 --port=11211 | 实时终端监控,类似 top 命令 | 显示命中率、连接数、item 数等动态指标;适合快速诊断。 |
| Prometheus Exporter | 使用官方或社区 exporter(如 memcached_exporter) 启动: memcached_exporter --memcached.address=localhost:11211 | 将 stats 指标暴露给 Prometheus 抓取 | 需配合 Prometheus + Grafana 实现长期监控与告警。 |
| Grafana Dashboard | 导入 ID 为 5035 的官方 Memcached dashboard | 可视化展示命中率、内存、evictions 等趋势 | 需提前配置 Prometheus 数据源。 |
| stats 命令脚本化 | 编写 shell 脚本定期执行 echo stats | nc localhost 11211 并解析输出 | 自定义监控逻辑,集成到 Zabbix/Nagios | 注意处理连接超时和格式变化。 |
| mcrouter stats | 若使用 mcrouter,可通过其 /stats HTTP 接口获取聚合指标 | 监控代理层性能与后端节点状态 | 支持 JSON 输出,便于自动化。 |
| 日志分析 | 启用 -v 或 -vv 输出请求日志,结合 ELK 分析慢操作或异常模式 | 适用于深度性能剖析 | 生产环境慎用高 verbose 级别,影响性能。 |
| netdata | 安装 netdata 后自动发现 Memcached 实例 | 开箱即用的 Web 监控面板 | 适合中小规模部署,资源占用低。 |
Exporter 关键指标示例(Prometheus):
memcached_commands_total{command="get"}→ get 总次数memcached_current_bytes→ 当前内存使用memcached_items_evicted_total→ 累计驱逐数memcached_current_connections→ 当前连接数
第八章:典型应用场景与最佳实践
8.1 Web 应用会话缓存
| 操作/概念名称 | 说明 | 注意事项 |
|---|---|---|
| 会话存储原理 | 将用户会话数据(如登录状态、权限)序列化后存入 Memcached,key 为 session_id | 替代文件或数据库存储,提升读写速度。 |
| Key 设计 | session:{session_id},如 session:abc123xyz | 避免 key 冲突;建议加命名空间前缀。 |
| 过期时间设置 | 与会话超时一致(如 30 分钟),通过 -m 控制内存,避免长期堆积 | 必须设置 exptime,否则依赖 LRU 可能提前淘汰活跃会话。 |
| 存储内容 | 用户 ID、角色、最后活动时间等轻量数据;避免存大对象 | 单个 session 建议 < 10KB。 |
| 安全性 | Memcached 无认证,需部署在内网;session_id 需强随机(如 UUID) | 防止会话劫持;不可暴露 Memcached 到公网。 |
| 失效处理 | 用户登出时主动 delete session:xxx;过期自动失效 | 主动删除可立即释放资源。 |
| 故障影响 | Memcached 宕机 → 所有用户强制重新登录 | 属于可接受降级(回源重建会话)。 |
| 适用框架示例 | PHP:session.save_handler = memcachedNode.js:express-session + connect-memjs | 需配置客户端连接池和重试机制。 |
8.2 数据库查询结果缓存
| 操作/概念名称 | 说明 | 注意事项 |
|---|---|---|
| 缓存键设计 | 使用 SQL 哈希或语义化 key,如 user:profile:1001 或 query:md5(SELECT * FROM users WHERE id=1001) | 避免 SQL 注入影响 key;推荐语义化 key 更易管理。 |
| 缓存流程 | 1. 应用查缓存 2. miss → 查 DB → set 缓存 3. hit → 直接返回 | 需处理 DB 查询失败时的缓存策略(如空值缓存)。 |
| 过期策略 | 设置合理 TTL(如 5~60 秒),或结合业务事件主动失效 | 高频更新表不宜缓存;静态数据可设长 TTL。 |
| 空结果缓存 | 对 DB 返回空的结果也缓存(如 set notfound:user:9999 "" exptime=10) | 防止缓存穿透;TTL 应较短(如 10 秒)。 |
| 更新一致性 | 数据变更时同步删除或更新缓存(Cache-Aside 模式) | Memcached 无事务,无法保证强一致;最终一致即可。 |
| 大结果集处理 | 避免缓存 > 500KB 的结果;可分页缓存或仅缓存 ID 列表 | 接近 1MB 限制易触发 SERVER_ERROR。 |
| 性能收益 | 减少 80%+ 的重复 DB 查询,尤其适用于读多写少场景 | 需监控命中率,低于 70% 需重新评估。 |
| 局限性 | 不支持复杂查询(如模糊搜索、聚合)的缓存 | 仅适用于主键或唯一索引查询。 |
8.3 分布式锁的替代方案讨论
| 方案名称 | 实现方式 | 在 Memcached 中的可行性 | 注意事项 |
|---|---|---|---|
| 基于 add 的简单锁 | add lock_key 0 10 1,成功表示获取锁;处理完后 delete lock_key | ✅ 可行 | 无自动续期;进程 crash 会导致死锁(依赖 exptime 释放)。 |
| CAS 乐观锁 | 先 gets 获取 CAS token,再 cas 更新 | ✅ 可行(用于数据更新) | 不适用于互斥执行场景,仅用于数据版本控制。 |
| Redlock(Redis 方案) | 多节点多数派加锁 | ❌ 不可行 | Memcached 无内置集群,无法实现跨节点共识。 |
| 带续期的锁 | 后台线程定期 replace 延长 exptime | ⚠️ 脆弱 | 网络分区或 GC pause 可能导致锁失效。 |
| 锁持有者标识 | value 存放唯一标识(如 UUID),释放时校验 | ✅ 推荐 | 防止误删其他进程的锁。 |
| 适用场景 | 低并发、短任务、允许偶尔失效的场景(如防重复提交) | — | 不适用于金融级强一致锁。 |
| 根本局限 | Memcached 无原子复合操作(如 Lua 脚本)、无持久化、无 Watch 机制 | — | 分布式锁应优先考虑 Redis、ZooKeeper、etcd。 |
结论:Memcached 可实现”尽力而为”的轻量级锁,但不推荐用于关键业务互斥控制。
8.4 缓存穿透/雪崩/击穿的应对策略(在 Memcached 中的局限性)
| 问题类型 | 定义 | Memcached 应对策略 | 局限性 |
|---|---|---|---|
| 缓存穿透 | 查询大量不存在的 key,请求直达 DB | 1. 空值缓存:对 DB 返回 null 的 key 缓存短 TTL(如 10 秒) 2. 布隆过滤器(应用层实现) | Memcached 本身无布隆过滤器;空值缓存增加内存压力。 |
| 缓存雪崩 | 大量 key 同时过期,瞬间高并发打垮 DB | 1. 随机 TTL:基础 TTL + 随机偏移(如 3600 ± 300 秒) 2. 永不过期 + 后台更新(应用层维护) | Memcached 无自动后台更新机制;需应用自行实现。 |
| 缓存击穿 | 热点 key 过期瞬间,大量并发请求争抢重建 | 1. 互斥重建:首个线程重建,其余等待(需分布式锁) 2. 逻辑过期:value 中嵌入过期时间,异步更新 | Memcached 无原子”get+set if miss”操作;互斥锁可靠性低(见 8.3)。 |
| 通用缓解措施 | — | • 限流(如令牌桶) • 降级(返回默认值) • 监控 evictions 和 miss rate | Memcached 本身不提供限流/降级能力,需应用层或网关实现。 |
| 根本缺陷 | — | Memcached 无以下能力: • 自动过期回调 • 发布/订阅通知 • 持久化保障 | 相比 Redis,应对复杂缓存问题的能力显著受限。 |
最佳实践建议:
- 对于高可靠缓存场景,优先选用 Redis;
- 若使用 Memcached,必须在应用层实现完整的缓存治理逻辑;
- 严格限制 Memcached 用于非关键、可降级、读密集型数据缓存。