为什么需要 Memcached 集群?
单台 Memcached 服务器的性能和容量是有限的,主要受限于以下几个因素:

- 内存容量:单台服务器的物理内存是有限的(256GB 已是上限),当应用需要缓存的数据量远超单机内存时,就需要多台服务器来分担。
- 网络带宽:即使内存足够,单台服务器的网络 I/O(输入/输出)能力也会成为瓶颈,无法处理海量的并发请求。
- 计算能力 (CPU):虽然 Memcached 是轻量级的,但在极高并发下,单 CPU 的处理能力也可能成为瓶颈。
- 高可用性:单点故障是最大的风险,如果唯一的 Memcached 服务器宕机,整个缓存系统就会失效,导致大量请求直接打到后端数据库,可能引发“雪崩效应”。
为了扩展容量、提升性能和保证高可用,我们必须将 Memcached 部署成一个集群。
Memcached 集群如何工作?(核心机制)
Memcached 集群最核心、最巧妙的设计在于其客户端机制。
a. 无中心化架构
Memcached 集群是一个无中心的分布式系统,集群中的所有 Memcached 服务器都是对等的,它们之间没有任何通信,也没有一个“主节点”来管理其他节点,每个节点只负责自己内存中的数据。
b. 数据分布:客户端一致性哈希
这是 Memcached 集群的灵魂,当一个客户端(如 PHP, Python, Java 应用)需要存储或获取一个键值对时,它会执行以下步骤:

- 获取 Key:客户端应用提供一个键,
"user:1001:profile"。 - 计算哈希:客户端会使用一个哈希算法(通常是 MD5 或 MurmurHash)对这个 Key 进行计算,得到一个哈希值(一个长整数)。
- 选择服务器:客户端会根据这个哈希值,从预先配置好的 Memcached 服务器列表中选择一个目标服务器。
- 最简单的做法(取模):
服务器索引 = 哈希值 % 服务器数量,这种方法在增减服务器时会导致大量的数据“雪崩迁移”(后面详述),所以基本不采用。 - 一致性哈希:这是目前的标准做法,一致性哈希会将哈希值空间(0 到 2^32-1)组织成一个虚拟的圆环,然后将每个 Memcached 服务器(通过其 IP:Port)也映射到这个环上的一个点,当需要存储一个 Key 时,计算其哈希值,然后找到环上顺时针方向第一个遇到的节点,数据就存储在该节点上。
- 最简单的做法(取模):
一致性哈希的优势: 当集群中增加或减少一个节点时,只会影响环上相邻的一小部分 Key 的映射关系,而不会导致整个集群的数据全部重新分布,这极大地减少了数据迁移的成本,保证了系统的稳定性。
如何构建和管理 Memcached 集群?
构建一个生产级的 Memcached 集群不仅仅是安装多个服务那么简单,还需要考虑架构、监控和运维。
a. 基本部署
-
安装:在多台服务器上分别安装 Memcached。
# CentOS/RHEL sudo yum install memcached # Ubuntu/Debian sudo apt-get install memcached
-
配置:每台服务器的配置文件(通常是
/etc/memcached.conf)中需要指定:
(图片来源网络,侵删)-l:监听的 IP 地址(通常是0.0.0或内网 IP)。-p:监听的端口号(默认11211)。-m:分配给 Memcached 的内存大小。-t:线程数。-u:运行用户。
-
启动:启动所有服务器上的 Memcached 服务。
sudo systemctl start memcached sudo systemctl enable memcached
b. 客户端配置
这是最关键的一步,你的应用程序所使用的 Memcached 客户端库(如 libmemcached, pymemcache, spymemcached 等)必须被正确配置。
- 服务器列表:在客户端配置中,你需要提供所有 Memcached 服务器的地址列表,
"10.0.0.1:11211, 10.0.0.2:11211, 10.0.0.3:11211"。 - 启用一致性哈希:确保客户端库的配置中启用了一致性哈希算法,大多数现代客户端库都默认使用此算法。
客户端示例 (Python pymemcache)
from pymemcache.client.base import Client
# 集群中所有服务器的地址
servers = [
('10.0.0.1', 11211),
('10.0.0.2', 11211),
('10.0.0.3', 11211),
]
# 创建客户端,它会自动处理一致性哈希
client = Client(servers, connect_timeout=1, timeout=1, key_prefix=b'myapp:')
# 存储数据
client.set('user:1001', b'{"name": "Alice"}', expire=3600)
# 获取数据
value = client.get('user:1001')
print(value) # 输出: b'{"name": "Alice"}'
c. 高可用性方案
Memcached 本身不提供主从复制或自动故障转移,如果一台节点宕机,客户端的一致性哈希算法会自动将原本指向该节点的请求重定向到环上的下一个健康节点。该节点上缓存的数据会丢失。
为了解决数据丢失问题,业界主要有两种方案:
-
客户端缓存穿透保护
- 原理:在应用层(客户端)实现一个本地的、短暂的缓存(如 Guava Cache, Caffeine),当请求一个 Key 时,先查本地缓存,再查 Memcached 集群,如果从 Memcached 集群中获取数据失败(比如节点宕机),应用可以: a. 直接向后端数据库查询。 b. 将从数据库查询到的结果同时写回本地缓存和 Memcached 集群中的另一个健康节点。
- 优点:实现相对简单,不引入额外组件。
- 缺点:保护范围仅限于当前应用实例,无法在多个应用实例间共享。
-
Magent (Proxy 方案)
- 原理:Magent 是一个轻量级的 Memcached 代理,它部署在 Memcached 集群的前面,客户端只连接 Magent,不直接连接 Memcached。
- 写操作:Magent 将写请求同时发送到两个不同的 Memcached 节点(主备)。
- 读操作:Magent 从其中一个节点读取数据。
- 优点:提供了数据冗余,当其中一个 Memcached 节点宕机时,数据不会丢失,实现了高可用。
- 缺点:增加了一层代理,带来了额外的性能开销和复杂性,Magent 项目现在维护较少,社区活跃度不高。
- 原理:Magent 是一个轻量级的 Memcached 代理,它部署在 Memcached 集群的前面,客户端只连接 Magent,不直接连接 Memcached。
集群监控与运维
管理一个集群,你需要时刻了解它的健康状况。
- 监控指标:
- 内存使用率:
stats命令中的limit_maxbytes和bytes_used。 - 连接数:
curr_connections。 - 命中率:
get_hits/ (get_hits+get_misses),这是衡量缓存效果最重要的指标。 - 命令执行数:
cmd_get,cmd_set。 - 网络 I/O:
bytes_read,bytes_written。
- 内存使用率:
- 监控工具:
memcached-tool:官方提供的命令行工具,用于查看统计信息。memcached-tool 10.0.0.1:11211 stats
- Prometheus + Grafana:业界标准的监控方案,通过
memcached_exporter抓取数据,在 Grafana 中创建可视化看板。 - Zabbix / Nagios:传统的企业级监控软件。
- 运维任务:
- 平滑扩容/缩容:这是 Memcached 集群运维的核心,通过一致性哈希,可以安全地添加或移除节点,添加节点后,部分数据会自动从旧节点迁移到新节点(由客户端在访问时触发)。
- 节点维护:在需要对某个节点进行维护(如重启、升级)时,应先使用
stats items和cachedump命令将其中的数据导出,然后将其从客户端的配置中移除,让流量自然散开,维护完成后,再将其加回集群。
Memcached vs. Redis 集群
在选择缓存技术时,Memcached 和 Redis 是最常见的两个选项,它们的集群模式有显著不同。
| 特性 | Memcached 集群 | Redis 集群 |
|---|---|---|
| 架构 | 客户端分片,无中心,客户端负责路由。 | 服务端分片,有中心(Cluster Manager),节点间通过 Gossip 协议通信,自动管理分片和故障转移。 |
| 数据复制 | 无原生复制,需要借助 Magent 等第三方方案实现高可用。 | 原生支持主从复制,每个主节点可以有多个从节点,提供自动故障转移。 |
| 数据类型 | 简单:仅支持 Key-Value,且 Value 只能是字符串/字节。 | 丰富:支持 String, Hash, List, Set, Sorted Set 等多种数据结构。 |
| 持久化 | 不支持,所有数据都存在内存中,重启即丢失。 | 支持,支持 RDB(快照)和 AOF(日志)两种持久化方式。 |
| 内存管理 | 静态分配,启动时分配 -m 指定的全部内存,LRU 策略淘汰数据。 |
动态分配,使用 maxmemory 和 maxmemory-policy 来管理内存,淘汰策略更灵活。 |
| 适用场景 | 多核、大内存、简单数据模型的超高性能缓存场景,如会话存储、页面片段缓存。 | 需要复杂数据结构、持久化、高可用的场景,如计数器、排行榜、分布式锁、消息队列等。 |
- 如果你只需要一个极致简单、高性能的键值缓存,并且数据模型非常简单,Memcached 集群是一个优秀的选择。
- 如果你需要数据结构、持久化、高可用性等更强大的功能,Redis 集群是更合适、更现代的选择。
希望这份详细的解释能帮助你全面理解 Memcached 服务器集群!
