Redis 和 Memcached 的区别?

#中间件#面试八股

Redis 和 Memcached 的区别?

结论:两者都是内存 KV,但定位已经分叉。Memcached 是纯粹的「多线程 + 内存对象缓存」,只做字符串缓存;Redis 是「单线程命令模型 + 丰富数据结构 + 可持久化 + 原生集群」的数据结构服务器。今天选 Memcached 的唯一理由是:价值极低、可随意丢失、纯字符串、需要吃满多核的极致读缓存,其余场景一律 Redis。

一、核心维度对照

维度RedisMemcached
数据模型String/List/Hash/Set/ZSet/Stream 等仅 key → bytes
线程模型命令执行单线程,6.0 起 IO 多线程多线程 worker,天然吃多核
持久化RDB、AOF、混合持久化不支持,重启即空
高可用主从、哨兵、Cluster无原生方案,靠客户端一致性哈希
集群分片16384 槽,服务端负责路由客户端分片,服务端互不通信
淘汰策略8 种(LRU/LFU/random/ttl/noeviction)近似 LRU(分段 LRU)
单 value 上限512 MB1 MB(默认,可调但影响 slab)
事务/脚本MULTI/EXEC、Lua、函数CAS(gets/cas)、incr/decr
过期惰性 + 定期删除,支持毫秒级惰性删除,到期不主动回收内存
内存管理jemalloc + 主动整理slab allocator,按 chunk 定长分配
协议RESP文本协议 + 二进制协议

二、线程模型差异决定了什么

Redis(6.0 之前)            Memcached
┌──────────────────┐        ┌───────────────────────────┐
│ 主线程            │        │ 主线程 accept              │
│  ├─ epoll 就绪    │        │   ├─ worker-1(epoll)     │
│  ├─ 解析/执行命令 │        │   ├─ worker-2(epoll)     │
│  └─ 回写响应      │        │   └─ worker-N(epoll)     │
└──────────────────┘        └───────────────────────────┘
一条命令串行执行              多连接并行执行,共享哈希表加锁

Redis 单线程命令执行换来的是无锁、无竞态、可原子组合(Lua、MULTI),代价是单实例只能压满一个核,需要靠多实例或 Cluster 横向扩展。Memcached 多线程能直接吃满多核,但跨 key 的原子操作只能靠 CAS 重试,做不到"读-改-写"一次原子完成。

三、内存分配与过期行为的差别

Memcached 的 slab allocator 把内存切成固定大小的 chunk,写入时按 value 大小落到对应 slab class。好处是无碎片、分配极快;坏处是不同 slab class 之间不能借内存,出现"总量没满但某类 chunk 用尽"的 OOM。过期 key 只是标记,不主动腾内存,只有新写入复用该位置时才回收。

Redis 用 jemalloc,key 到期后由惰性删除 + 定期删除回收,配合 maxmemory 触发淘汰,能做到"内存到顶就按策略挤掉旧数据"。

# Memcached 常用启动参数
memcached -m 4096 -c 8192 -t 8 -I 2m
# -m 内存 MB,-c 最大连接数,-t worker 线程数,-I 单 item 上限

# Redis 对应能力靠配置 + 命令
redis-cli CONFIG SET maxmemory 4gb
redis-cli CONFIG SET maxmemory-policy allkeys-lru
redis-cli INFO memory | grep used_memory_human
# redis.conf 关键片段
maxmemory 4gb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec

四、什么情况下真的会选 Memcached

场景特征建议原因
纯 HTML 片段 / 小对象缓存,命中即返回可以 Memcached多线程吃满多核,逻辑极简
需要计数器、排行榜、去重集合RedisMemcached 只有 incr,复杂结构要应用层拼
缓存需要高可用、扩容不丢数据RedisMemcached 客户端分片扩容要搬 key
一次请求要读写多个 key 且需原子RedisLua / MULTI 一次往返完成
数据允许全丢、只要最快吞吐两者都可,看压测同机同连接下差异通常在 10% 量级

五、常见追问

  • 问:既然 Redis 单线程,为什么吞吐还比 Memcached 高?答:通常不是单机极限更高,而是 Redis 省掉了网络往返次数——一次 Lua 或一个 ZSet 命令能做到 Memcached 要多次 get/set 才能完成的事。
  • 问:Memcached 支持集群吗?答:服务端不支持互相通信,分片由客户端(如 spymemcached 的一致性哈希)完成,扩缩容时需处理数据倾斜。
  • 问:多线程一定更快吗?答:在纯 GET/SET 的短连接压测中 Memcached 有优势;但业务一旦需要原子计数、有序集合、分布式锁,Redis 少一次网络交互就赢回来了。
  • 问:Memcached 的 CAS 和 Redis 的 WATCH 有何不同?答:CAS 是单 key 版本号比较后写入,WATCH 是监视多个 key、EXEC 时整体失败,后者不能回滚已执行的命令。
  • 问:能否只用 Redis 做纯缓存,关掉持久化?答:可以,save "" + appendonly no,此时它就是个功能更多的 Memcached。
  • 问:Memcached 的 1 MB 上限怎么绕过?答:调 -I 提高单 item 上限,但会新增更大的 slab class,挤压其他类的内存;更常见的做法是把大对象压缩或拆成多个 key。

小结

  • 差异的根在三点:数据模型(字节 vs 结构)、线程模型(串行 vs 并行)、集群与持久化能力。
  • Memcached 的 slab 与多线程在小 value 纯缓存上仍有价值,但没有持久化、没有原生分片是硬伤。
  • 现代架构默认选 Redis;只有明确"缓存可全丢、只要字符串、要压满多核"时,才值得把 Memcached 拿出来比较。
笔记加载中…