做后端架构这些年,被问得最多的问题,一个是“为什么 Redis 都出 Cluster 了,还有人在用 Memcached”,另一个是“这俩都是分布式缓存,到底差在哪”。这两个问题的答案,恰恰藏在它们的架构差异里。Redis Cluster 是服务端主导的分片加高可用方案,Memcached 则是把分布式策略完全前置到客户端,服务器本身保持极简。再加上 Memcached 的 slab 分配器这种内存管理机制,两者的行为模式、运维方式、故障表现完全不同。这篇文章我想从架构设计的取舍出发,把 Redis Cluster 和 Memcached 的差异拆开揉碎,顺带说说那些文档不会写、但线上一定会遇到的细节。
1. 先建立坐标系:这两个缓存系统到底在解决什么问题
很多人一听到“分布式缓存”,下意识就把 Redis 和 Memcached 拉到同一张对比表里,然后逐项比较读写速度、线程模型、支持的数据类型。这当然有参考价值,但很容易忽略一个前提:Redis Cluster 和 Memcached 从诞生那天起就不是同一类物种,它们解决的问题域不同,优势边界也不同。
1.1 Redis Cluster 的定位
Redis 本身是单机内存数据结构服务,支持字符串、Hash、List、Set、ZSet 等丰富结构,并且能做持久化。单机 Redis 能扛的容量取决于物理内存,流量一大或者数据量一大,就必然要往分布式架构演进。Redis Cluster 正是官方提供的分布式形态:通过 16384 个槽位把数据打散到多个主节点上,每个主节点又可以挂从节点做冗余,节点之间用 Gossip 协议维持对集群状态的认知,客户端访问任意节点都能通过重定向拿到正确数据。
这个设计决定了 Redis Cluster 不是一个简单的“多个 Redis 拼起来”,它引入了槽位路由、集群总线、故障检测与自动故障转移机制,本质上是把“协调者”的角色隐式地嵌进了节点自身。好处是没有独立协调组件,部署简单,坏处是节点数量越多,Gossip 消息量越大,集群内部的协调成本也在上升。
1.2 Memcached 的定位
Memcached 要朴素得多:它就是一块巨大的分布式内存哈希表,value 在最经典的用法里就是一段字节流。服务器端不负责数据分布在哪个节点,不维护集群成员关系,也没有故障转移的概念。所有分片逻辑都在客户端实现,客户端通过一致性哈希算法把 key 映射到某个 Memcached 节点,请求直接打过去,节点只负责存和取。
这种极简架构有非常明显的历史背景。Memcached 进入大众视野时,互联网应用正在经历一轮数据库压力爆发,大家急需一个“扛得住高并发读取”的纯缓存层,而不是一个功能繁重的数据底座。因此它把“分布式复杂性”推给了客户端,换来的是服务器端极低的延迟、极稳定的读取表现,以及非常好的横向扩展能力——加机器只需要更新客户端配置。
1.3 架构差异背后的取舍逻辑
理解了定位差异,再看两者的架构比较就清晰了。Redis Cluster 更像是一个完整的数据平台,它帮用户承担了路由、高可用、部分集群管理职责,适合你对数据可靠性有要求、或者业务需要复杂数据结构的场景。Memcached 则像一个随时可以腾出空间的临时仓库,它不承诺持久化,不在节点间复制数据,节点挂了缓存就没了,而且这种“消失”很容易被业务接受——因为缓存本来就是可以重建的。
这个取舍逻辑非常重要,因为很多人选型时会陷入“Redis 功能多,所以一定更好”的误区。实际上,如果你的业务只需要简单的 key-value 读取,且对缓存命中率极度敏感,Memcached 的简洁反而能带来更低的运维复杂度和更稳定的性能表现。没有唯一正确的缓存,只有符合当前业务阶段和团队能力的缓存。
2. 数据分发机制:槽位映射与一致性哈希的分水岭
分布式缓存核心要解决的一个问题是:给定一个 key,到底去哪台机器上找数据?这个“寻路”机制直接决定了客户端复杂度、扩缩容成本、以及数据迁移方式。Redis Cluster 和 Memcached 在这个问题上走的是两条完全不同的路线。
2.1 Redis Cluster:槽位映射与服务端路由
Redis Cluster 把整个 keyspace 划分为 16384 个哈希槽,每个 key 通过 CRC16 算法计算出一个 16 位的值,再对 16384 取模,得到槽位编号。每个主节点负责一段连续的槽位区间,比如三节点集群常见的分配是 0-5460、5461-10922、10923-16383。槽位和数据是绑定的,key 永远落在一个固定槽,槽位又明确属于某个节点,所以服务端能做精确路由。
客户端请求任何一个节点时,如果 key 对应的槽正好在本节点,就直接处理;如果不在,节点会返回 MOVED 重定向错误,并带上目标节点的地址。这里有一个容易踩的坑:普通模式下,客户端拿到 MOVED 后如果不重新发起请求,这次操作就失败了。这也是为什么官方提供了redis-cli -c这种集群模式客户端,以及各种语言 SDK 都内置了槽位缓存和重定向跟随逻辑。
在集群模式下,多个 key 的操作会受到槽位置约束。Redis Cluster 只保证同一槽内的多个 key 能被原子执行,跨槽的 multi-key 操作(比如MGET、DEL多个 key)默认是不允许的,除非这些 key 带有相同的 hash tag。hash tag 的做法是:key 里用花括号把某一段包起来,比如user:{9527}:followers,Redis 在计算槽位时只对花括号里的部分做 CRC16。我见过不少团队把这条规则理解成“所有 key 带 hashtag 就行”,结果为了跨槽事务把所有 key 塞进同一个花括号,最终导致数据倾斜,一个节点扛了几乎全部流量。
2.2 Memcached:客户端分区与一致性哈希
Memcached 没有任何服务端路由,客户端拿到 key 后,先通过哈希函数算出一个值,再用一致性哈希环决定这个 key 属于哪台服务器。所谓一致性哈希,是把服务器节点映射到一个 0 到 2^32-1 的环形空间中,key 的哈希值也落在环上,然后顺时针找到第一个服务器节点。相比简单的取模算法,一致性哈希最大的优势是:当节点增删时,只有环上相邻区间的 key 需要重新映射,大部分 key 不受影响。
实际工程里的实现会比教科书复杂一些。为了均衡负载,客户端通常会给每个物理节点生成多个虚拟节点(比如 160 个),让它们在哈希环上均匀分布,否则节点少时容易产生倾斜。开源的 ketama 算法是这个领域的经典实现,很多语言和代理层(比如 twemproxy)都借鉴了它的思路。另有 mcrouter 这类 Facebook 开源的 Memcached 路由代理,它把一致性哈希从应用客户端收拢到了代理层,这样业务方不用在每个语言里各自维护分片逻辑,但也引入了一个新的代理层,需要额外部署和保障它的高可用。
2.3 集群扩容时两者分别会发生什么
Redis Cluster 在线扩容是节奏化的:新增节点后,通过redis-cli --cluster reshard指定要迁移多少槽位,然后数据以槽为单位在不同节点间搬运。移动过程中,源节点负责把数据搬运到目标节点,并继续服务读请求;迁移完成前后,节点会通过 ASK 重定向告知客户端去哪个节点访问旧数据或者新数据。整个流程可以做得很平滑,但槽位迁移的粒度、并发度都需要精细控制,迁移期间网络和 CPU 开销会明显上升。
Memcached 扩容则非常直接:新节点启动后加入一致性哈希环,客户端重新初始化哈希环,一部分 key 的映射关系发生变化。这里有个冷酷的现实——Memcached 节点之间不复制数据,新增节点后,原先落在 A 节点的部分 key 重新映射到了新节点,那部分对应的旧缓存会变成“孤儿数据”,客户端再访问时会发现 miss,需要回源数据库重建缓存。这意味着 Memcached 扩容必然伴随一次小规模的缓存失效,如果被牵涉的 key 流量特别大,会产生一次缓存击穿压力。团队在做扩容时,必须选择一个流量低谷,并准备好数据库端的限流与降级策略。
3. 内存管理的内功:从 Memcached Slab 到 Redis 内存模型
内存是缓存系统的命根子。一个缓存节点能存多少数据、会不会被逐出、内存碎片有多严重,这些都会直接反映在命中率和高可用性上。Redis Cluster 和 Memcached 的内存管理哲学完全不同,其中最值得展开的就是 Memcached 的 slab 分配器。
3.1 Memcached 的 Slab Allocator 是怎样运作的
很多人第一次听到“slab”这个概念时,容易把它理解成一种简单的内存池,其实 slab 分配器的设计初衷,是解决长时间运行后的内存碎片问题。如果每次 set 一个 value 都用 malloc 分配一块精确大小的内存,删除后再 free,内存会变得七零八落,系统性能会下降。Memcached 的做法是:启动时把内存划分为一系列 slab class,每个 slab class 管理一组固定大小的内存块,不同 class 的块大小按比例递增,常见的增长因子是 1.25。
比如 slab class 1 的块大小是 96 字节,class 2 是 120 字节,class 3 是 152 字节,依此类推。存入一个 100 字节的 value 时,Memcached 会把它放进 120 字节的块里。这种方式大幅减少了内存分配次数和碎片,但代价是内存浪费:100 字节的数据占用 120 字节块,那 20 字节就成了内部碎片。如果你 start 时的-f增长因子设置不当,比如设得过大,内部碎片率会非常难看。
每个 slab class 内部的块会组织成 page,page 默认大小是 1MB。Memcached 初始化时并不把内存均匀预分给所有 class,而是按需分配,某个 class 的 page 用完后再向全局内存申请新 page。当一个 class 的内存配额不够时,它只能通过 LRU 驱逐旧数据来腾位置,不能动用其他 class 的空闲块。这里有个典型问题:如果业务 key 的 value 大小分布非常分散,大 value 会占据某几个 slab class 的 page,小 value 所在的 class 即使还有很多内存余量,大 value 对应的 class 也可能会频繁逐出,导致命中率骤降。我调过的一个案例里,就是因为某个业务把 100KB 的大对象也往 Memcached 里塞,导致负责 96KB 以上块大小的 slab class 反复驱逐,而其他 class 还有大量空闲,缓存命中率掉到不到 50%。
3.2 Redis 的内存管理和与众不同的淘汰策略
Redis 的内存分配默认依赖 jemalloc,和 Memcached 固定块大小分配不同,jemalloc 会按实际需要采用多级 size class 分配,兼顾减少碎片和降低分配开销。Redis 对整个内存没有一个“预分区”的概念,所有 key 共享同一片内存空间,这在内存使用弹性上更友好。但 Redis 的内存淘汰机制是全局的,当maxmemory达到阈值时,会根据配置的淘汰策略挑选 key 逐出。
常见的淘汰策略包括 noeviction、allkeys-lru、volatile-lru、allkeys-lfu、volatile-lfu 等。比如 allkeys-lru 会对整个 keyspace 按 LRU 近似算法淘汰,而 volatile-lru 只淘汰设置了过期时间的 key。Redis 的 LRU 并不是严格意义上的真实 LRU,而是采样近似算法,默认采样 5 个 key 挑最久没访问的淘汰,通过maxmemory-samples可以调整采样数。采样数越大结果越精确,但淘汰时的 CPU 开销也越高,生产环境通常不建议盲目调到 10 以上。
这项差异在 Redis Cluster 中会被放大:由于集群节点的内存管理是独立的,全局 maxmemory 策略是每个节点各自生效,数据倾斜会导致某个节点先触发淘汰,而其他节点还很空闲。因此做容量规划时不能简单用“总数据量除以节点数”,要留出足够的倾斜余量。
3.3 内存碎片与大 Value 处理
Redis 和 Memcached 都有内存碎片问题,只是来源不同。Memcached 的 slab 分配器通过固定块来避免外部碎片,但引入了内部碎片,而且 slab class 之间不能借用内存,资源不能被高效整合。Redis 用 jemalloc 做精细分配,外部碎片平均更小,但遇到大量频繁更新且长度多变的 value 时,也会出现碎片率上涨。Redis 4.0 以后支持MEMORY PURGE来整理碎片,也有自动碎片整理机制,但开启后对峰值 QPS 有一定影响,建议在低峰期操作。
大 value 对两者都是雷区。Memcached 的默认最大 value 长度是 1MB,超过会直接报错,需要调整-I参数。Redis 的单个 value 理论上可以到 512MB,但这会带来序列化成本、内存拷贝成本和网络传输压力。在分布式架构里,一个超大 key 还会导致槽位所在节点负载急剧上升,并且一旦集群需要迁移槽位,大 key 迁移会拖慢整个 reshard 流程。我的建议是:任何缓存系统都别放超过 100KB 的 value 型数据,如果业务确实要缓存大文本或大对象,先做业务层压缩,或者把大对象拆成小数据块,只有索引和热数据进缓存。
4. 高可用、数据安全与一致性模型
如果说前几部分还只是“实现细节差异”,那到高可用和一致性,就是影响线上事故级别的分水岭了。很多人把 Redis Cluster 当成“高可用缓存”的默认解,把 Memcached 当成“临时但不可靠的缓存”,这个判断大方向没问题,但细节远比这个粗糙结论复杂。
4.1 Redis Cluster 的高可用能力与故障转移
Redis Cluster 的每个主节点可以配置多个从节点,从节点实时复制主节点的数据。当主节点发生故障时,集群需要先完成客观下线判定:集群中的节点通过 Gossip 消息互相交换状态,如果某个节点被大多数持有它槽位的主节点标记为疑似下线,才会被确认为客观下线,然后触发自动故障转移。
自动故障转移的本质是从节点发起选举,竞选出新的主节点。这里有几个关键参数:cluster-node-timeout控制节点间通信超时,默认 15000 毫秒;故障转移的开销由延迟和从节点的复制进度共同决定。从节点会优先选择复制偏移量最新(也就是数据最接近主节点)的节点来晋升。整个设计思路是尽量做到主从切换后不丢数据,但要注意,如果主节点宕机时数据还没来得及同步给从节点,这部分数据就是丢失的。
还有一点容易被忽视:Redis Cluster 的读写都走主节点,从节点默认只做冗余和分担部分只读流量,需要通过READONLY命令显式开启读分摊。我见过有些团队把从节点当成“第二个主节点”来抗读,结果从节点读到的是稍旧的数据,业务把缓存写回和读取的时序理解错了,出现奇怪的脏读现象。缓存场景读旧一点往往能接受,但你需要明确这一点是有意为之,而不是误配。
4.2 Memcached 的故障语义:缓存失效的代价
Memcached 没有主从复制,也没有自动故障转移。一个节点宕机,它之前承载的 key 全部丢失,客户端一致性哈希环上对应的虚拟节点会摘除,原先映射到该节点的请求会被重新路由到相邻节点。这会产生两个问题:一是相邻节点会突然承受额外的请求压力,如果该节点本身已经接近内存上限,会发生连锁驱逐,进一步拉低整体命中率;二是故障节点上的缓存数据需要回源数据库重建,流量可能瞬间集中到数据库上,引发慢查询甚至宕机。
在 Memcached 架构里,“故障”不是可选的极端情况,而是运维常态。因此业务侧的缓存重建机制必须做得足够健壮。我自己的习惯是:回源数据库前先做请求合并和本地短时缓存(比如 1 秒内的进程内锁),避免同一时刻大量线程同时回源。这个手段在 Redis 单机或者 Cluster 故障转移期间同样适用,只是 Memcached 的故障频率通常更高,更需要这种兜底方案。
4.3 一致性模型与原子操作
Redis 是单线程模型,所有指令在单节点上是串行执行,这一点保证了单个 key 的操作原子性。Redis 还提供 Lua 脚本和事务来保证多个 key 的原子性。但在 Cluster 模式下,事务和 Lua 脚本都有一个限制:涉及的 key 必须都在同一个槽。这一点前面提过,但它是理解 Redis Cluster 一致性的关键。你能保证的强一致性范围,是以槽为边界的;跨槽的一致性,基本靠业务补偿。
Memoized 的单个 key 操作在服务器上同样是原子的,因为 Memcached 也有类似并发的锁机制。但 Memcached 没有事务,也没有 Lua 脚本,它给业务提供的就是 get、set、add、replace、append、prepend、cas 这些指令。其中 cas 是一个有实际价值的原子操作:它通过版本号标记 value,执行 cas 时对比版本号,如果版本号变了就失败。这个能力可以用来实现分布式锁或者乐观更新的缓存。比如多个客户端并发更新同一份缓存时,用 cas 保证只有一个客户端能写进去,避免后写覆盖先写的脏数据问题。
不过,Memcached 的数据一致性近似“最终可丢”模型,它不保证异常情况下的缓存数据仍然存在。所以我一直认为,它的定位应该是“可丢失、可重建、可容忍短期不一致”的加速层,而不是“数据重要不能丢”的存储层。这个边界想清楚,Memcached 其实很安稳。
5. 分布式缓存选型的实用清单
聊了这么多架构差异,最终要落到“我们系统到底该选谁”。选型没有银弹,但根据我的实战观察,大多数团队纠结的其实不是技术参数,而是把业务需求映射到架构能力的匹配度。
5.1 用 Redis Cluster 的场景
如果你的业务出现下面这些特征,Redis Cluster 是更稳妥的选择:需要存储的数据结构不仅仅是字符串,还涉及 Hash、ZSet、Set、List 等;需要分布式环境下做延时队列、发布订阅、分布式锁、排行榜等复杂操作;或者业务希望缓存系统具备一定持久化能力,哪怕只是周期性 RDB 快照,也能帮助冷启动时快速预热。
另一个常被忽略的因素是团队运维能力。Redis Cluster 的安装、节点管理、槽位迁移、故障转移调优,都需要更细的运维技能和监控颗粒度。如果团队已经有比较成熟的 Redis 运维体系,Cluster 带来的复杂度是可控的。比如我用redis-cli --cluster check做过常规巡检,用CLUSTER INFO观察集群状态,这些工具本身都比较成熟,但要做到生产级可靠,还必须有内存、延迟、慢日志等监控配合。
5.2 用 Memcached 的场景
Memcached 更适合的往往是那些“要缓存,但不想背太多概念包袱”的场景:value 结构简单,基本都是序列化后的字符串或二进制,只做 get/set,不需要事务和 Lua,也不需要 Redis 的数据结构。如果你的团队使用的是多语言异构系统,想让 Java、Go、Python 服务共享同一套缓存,Memcached 的客户端分片反而更轻,因为每个语言客户端只需要维护一致性哈希即可。
从性能指标看,Memcached 在纯读场景下延迟非常稳,因为它的线程模型是多线程处理 I/O,而且没有持久化和 RDB fork 引起的阻塞,不会有 Redis 在 bgsave 时出现的瞬时抖动。另外它的内存管理是 slab 池式分配,启动后内存预占明确,部署和容量评估都简单。如果你的集群规模不算小,而且缓存 access pattern 非常规整,Memcached 是一个性价比很高的选择。
5.3 两种方案的混用与迁移案例
现实中很多系统不会二选一,而是把两者组合使用。一个常见组合:热点数据、排序类数据、分布式锁等放入 Redis Cluster,简单但量大的 key-value 型数据放入 Memcached,让两种系统各自发挥优势。过渡层可以先用统一封装类屏蔽底层差异,上层只暴露 get/set/delete 接口,这样后续要迁移也不至于伤筋动骨。
我做过一次从 Memcached 迁移到 Redis Cluster 的改造,最大的坑并不是数据搬运,而是应用代码的语义差异。Memcached 的 add 是“key 不存在时才成功”,Redis 里有SETNX;Memcached 的 cas 对应 Redis 的WATCH/MULTI/EXEC或者 Lua 脚本;Memcached 对过期时间超过 30 天会称为绝对时间戳,Redis 的EXPIRE接受相对秒数。这些语义细节如果不逐个对照梳理,线上很容易在迁移后发现某些操作行为不一致。所以要迁移,先把客户端封装层做厚,再按流速分批发布,别指望一把梭。
6. 实战中的坑与排查记录
架构差异讲得再多,都不如记录一些实际踩过的坑来得有用。这几条是我在线上环境真实遇到并排查过的问题,希望能帮你少走一段弯路。
6.1 Redis Cluster 跨 Slot 操作的坑
曾经有个活动系统使用了 Redis Cluster,业务团队为了让一次活动页的多个 key 在事务里原子更新,给所有 key 加了同一个 hash tag,比如act:2024bx:{20240701}:awards。结果活动上线后,某个节点 CPU 接近打满,其他节点却很空闲。排查后发现,所有 key 都被映射到同一个槽位,流量和内存全部集中在一个节点上。这个案例的教训是:hash tag 是解决跨 key 原子性的工具,但使用前必须预判 key 的分布宽度,尤其要避免把所有用户相关的 key 都塞进同一个 hashtag。
另一个常见问题是客户端未正确跟随 MOVED 重定向。如果使用比较老的客户端版本,或者自行实现协议解析时没有处理 ASK 重定向,缓存操作会在数据迁移期间频繁失败。定位这种问题很简单,开启客户端日志,观察是否大量出现MOVED或ASK响应,再看看 SDK 版本是否支持集群模式。
6.2 Memcached Slab 内存浪费与逐出异常
前面提到的 slab 内存不均衡是我见过最多的问题。定位方法其实不难:通过stats slabs查看每个 slab class 的内存分配情况,通过stats items查看每个 class 的 item 数量和过期情况。如果发现某个 class 的evicted数量持续增加,而其他 class 还有空闲 page,就能基本确认是 value 大小分布不均导致的局部驱逐。
解决办法有几种:如果 value 大小本来就非常分散,可以考虑将大 value 拆分为多个小 value 存储,然后客户端重组,避免大对象长期占用某几个 slab class;或者在业务层做大小分级,不同大小的数据使用不同的缓存实例。还可以调-f增长因子,比如从默认 1.25 改成 1.15,让 slab class 粒度更细,减少内部碎片,但这会带来更多 slab class,内存管理 overhead 也会增加。
6.3 缓存穿透、击穿、雪崩的兜底套路
无论选哪一种分布式缓存,这三个问题都躲不掉。缓存穿透是指请求一个完全不存在的数据,缓存和数据库都没有,导致每次请求都打到数据库;处理办法是缓存空值,并设置短过期时间,或者在业务入口做布隆过滤器拦截。缓存击穿是指某个热点 key 过期瞬间,大量并发请求同时回源;处理办法是加互斥锁,同一时刻只允许一个线程回源建缓存。
缓存雪崩是指大面积 key 在同一时间段过期,或者缓存节点整体故障导致大量请求直冲数据库。对付过期导致的雪崩,可以在业务中给缓存过期时间加随机抖动,比如基础 TTL 上叠加 1%-5% 随机量,让过期时间自然分散。对付节点故障导致的雪崩,就得靠部署层面的容灾和限流,比如 Redis Cluster 的从节点切换、Memcached 的多节点冗余,以及统一接入层的 Rate Limiter。
7. 常见问题速查表
平时沙龙里总有人问一些反复出现的问题,我整理成一个速查表放在这里,方便直接对应故障现象和处理思路。
| 现象 | 可能原因 | 排查工具与手段 | 处理建议 |
|---|---|---|---|
| Redis Cluster 某些节点负载极高 | 槽位分配不均匀或 key 分布倾斜 | CLUSTER NODES、CLUSTER SLOTS配合热点 key 分析 | 执行 reshard,必要时拆分 hashtag 使用范围 |
| Redis Cluster 大量 MOVED 响应 | 客户端未正确缓存槽位表或节点变更中 | 开启客户端集群模式,检查 SDK 是否自动跟随重定向 | 升级客户端版本或检查连接配置 |
| Memcached 命中率突然下降 | 节点重启、扩缩容导致 key 重新映射 | stats观察命中率,stats items查看驱逐情况 | 扩容前评估影响面,做好请求合并回源 |
| Memcached 某个 slab class 持续驱逐 | Value 大小分布不均,局部内存紧张 | stats slabs比较各 class 分配情况 | 拆大 value,或调低-f增长因子 |
| 缓存节点重启后大量回源 | 缓存数据丢失、冷启动无数据 | 监控回源量与数据库 QPS | 提前预热热点 key,或并行做本地缓存兜底 |
| 缓存过期瞬间数据库压力尖峰 | 大量 key 同时过期 | 检查 key 的 TTL 分布 | 过期时间加随机抖动,打散过期时刻 |
| 跨槽 multi-key 操作报错 | 槽位不在同一节点,跨槽事务受限 | 检查报错信息和 key 的槽位计算结果 | 使用 hash tag,并确认 key 分布仍均匀 |
这个表格不是给你背下来的,而是建议收藏一份,等线上出问题时再对照。有些问题光看名称很难第一时间想到根因,比如 Memcached 命中率下降,表象是业务流量上涨,实际是 slab class 的内存分配出现了结构性失衡。这类问题需要一定的经验积累,但方向对了,排查速度能快很多。
最后再多说一句个人体会:做分布式缓存选型,别只看对方的宣传口径,也别只盯着 benchmark 数字。Redis Cluster 和 Memcached 的架构差异,背后是一整套关于一致性、可用性、运维复杂度、业务形态的取舍。如果你能先把业务偏好看清楚,再回头技术选型,其实很多纠结都会自己消散。缓存这件事,真正重要的不是工具多强大,而是你对它的行为语义有多熟悉。