读完 缓存架构
的 Cache-Aside 与击穿治理,或 缓存工程
的多级缓存之后,工程师往往已经会「在应用旁挂一层
Redis」。但若被追问 进程内 HashMap
与网络缓存的边界在哪、Memcached 与 Redis
差的是线程还是数据结构、嵌入 RocksDB 何时该替代
Redis,答案容易滑向「看场景」而缺少可核对的机制地图。
本文是「Redis / 缓存内核」系列第 1 篇。目标不是重复五种读写模式全文,而是回答三个定位问题:
- 进程内 HashMap、Memcached、Redis、RocksDB 嵌入 KV 在同一问题轴上各占哪一段?
- 本系列与 architecture/17、storage/66、linux/redis-source(Redis 4.0 Org 情景)、libevent redis-lite 如何分工?
- 后续 16 篇按什么依赖顺序读,才能把「一条命令落到内存对象」串起来?
本文是「Redis / 缓存内核」系列第 1 篇(共 16 篇)。→ 系列目录
篇目 核心内容 第 1 篇 · 缓存服务器全景 四类 KV 生态位、相邻系列分工、16 篇地图 第 2 篇 · 事件循环与 I/O aeEventLoop、客户端缓冲、IO threads 边界第 3 篇 · 对象模型与 SDS redisObject、type/encoding、SDS
版本锚定:Redis OSS 7.4 / 8.x(本系列源码路径与官方文档);Memcached 作第 14 篇对照;linux/redis-source 锚定 4.0 历史路径。
环境说明:本篇以架构定位与文献谱系为主,不粘贴未执行的
redis-benchmark或INFO数字;机制边界来自 Redis 官方文档与 7.4.x 源码结构。
一、四类内存 KV 生态位
把「缓存 / KV」按 部署边界 与 值语义 粗分四格,便于后续选型与读源码时不混层。
flowchart LR
subgraph process["In-Process"]
HM["HashMap / ConcurrentMap"]
RK["RocksDB / LevelDB embed"]
end
subgraph network["Network Service"]
MC["Memcached<br/>opaque blob + slab"]
RD["Redis<br/>typed objects + optional persistence"]
end
HM -->|"miss / capacity"| MC
HM -->|"local durable state"| RK
MC -.->|"richer types / persistence"| RD
| 形态 | 典型 API | 值模型 | 持久化 | 多进程共享 | 主要代价 |
|---|---|---|---|---|---|
| 进程内 HashMap | map.put(k,v) |
语言原生对象 | 无(随进程消失) | 否 | 堆内存、GC/STW、无跨实例一致性 |
| Memcached | get/set
+ slab |
opaque blob | 默认无 | 是(分片在客户端) | 功能单一;多线程与 slab 碎片(第 14 篇) |
| Redis | RESP 命令 | string/list/hash/zset/stream… | RDB/AOF 可选 | 是(单机/Cluster) | 单主线程执行命令 + 编码/持久化复杂度 |
| RocksDB 嵌入 KV | Put/Get/Iterator |
字节序列 + CF | WAL + SST 默认有 | 否(库嵌入进程) | Compaction、写 stall、运维在宿主进程内 |
1.1 进程内 HashMap:零跳数,零共享
应用内 ConcurrentHashMap、Guava
Cache、Caffeine 等落在
同一
JVM/进程地址空间。优势是延迟最低、无序列化
hop;代价是
无法跨实例共享、容量受单进程堆约束、淘汰策略与观测性由语言运行时决定。
architecture/17
中的 L1 本地缓存多在这一格; storage/66
讨论它与 Page Cache、远程缓存的叠加顺序。
边界:HashMap 解决「本进程重复读」;一旦需要 多副本一致视图 或 独立扩缩容,必须上网络层(Memcached/Redis)或带复制/分片的存储服务。
1.2 Memcached:纯分布式缓存
Memcached 把值当作
不透明的字节块,分配器以 slab
class 切分内存(源码
slabs.c;设计讨论见 Nishtala et al., NSDI
2013,B 级)。多 worker
线程处理连接;无内置持久化、无复杂数据结构。Facebook
大规模部署经验(同论文)强调 客户端分片、UDP 聚合
get、invalidation 与 MySQL 的配合——这与 Redis
Cluster 的服务端 slot 模型不同(第 13 篇对照)。
1.3 Redis:内存数据结构服务器
Redis 官方文档与源码把产品定位为 in-memory data
structure store:除 string 外还有
list、hash、sorted set、stream 等
带语义的操作(如
ZADD、XREADGROUP),并可选
RDB/AOF(第 9–11
篇)。执行模型上,命令仍在主线程上突变共享数据集;7.x
起的 IO threads 只卸载读写与解析,不改变这一不变量(第 2
篇)。
1.4 RocksDB 嵌入 KV:磁盘 LSM,不是网络缓存
RocksDB 内核系列 拆解的是 写优化 LSM:WAL 顺序写、MemTable、后台 compaction。它嵌入 TiKV、Flink state backend 等进程,提供 可持久、可迭代 的本地 KV,而不是替代 Memcached 的纯 RAM 网络缓存。
| 需求信号 | 更可能的一格 |
|---|---|
| 微秒级、可丢、跨实例共享 | Memcached 或 Redis(volatile) |
| 复杂结构 + 可选持久化 + 复制 | Redis |
| 单进程大量写、需磁盘耐久、范围扫描 | RocksDB 嵌入 |
| 仅本进程、无网络 | HashMap / 嵌入库 |
二、与相邻系列的分工
本系列刻意 不重写 应用层缓存模式与 LSM 公式全文,而是引相邻篇目,把读者送到已写透的层。
| 话题 | 相邻系列 | 本系列承接点 |
|---|---|---|
| Cache-Aside、击穿/雪崩、热点 | architecture/17 | 第 1、14、16 篇:模式 ↔︎ 服务器能力边界 |
| Page Cache → 应用缓存栈 | storage/66 | 第 1、8 篇:远程缓存与 OS 页缓存分工 |
| 哈希表 / 跳表 / LRU 理论 | algorithms/07、18、52 | 第 4、6、8 篇:Redis 工程编码与近似淘汰 |
| Redis 4.0 Org 情景漫游 | linux/redis-source | 第 1 篇历史入口;机制以 7.4/8.x 为准 |
| RESP + libevent 玩具服务器 | libevent redis-lite | 第 1–2 篇:协议入门 vs 生产
ae 路径 |
| 磁盘 LSM、WAL、compaction | rocksdb、lsm-tree | 第 1 篇对照;不把 Redis 当 LSM 引擎讲 |
| fsync / 持久化语义 | fsyncgate、storage/12 | 第 11 篇落地配置 |
linux/redis-source 基于 Redis 4.0 的 Org 笔记,适合建立「事件循环 + dict + object」的第一遍直觉;但 listpack 替代 ziplist、IO threads、客户端内存限制 等已在 7.x 演进。本系列正文以 Redis Ltd. 7.4 / 8.x tag 源码为准,4.0 仅作谱系参照。
libevent
redis-lite 用 bufferevent +
unordered_map 实现最小 RESP,帮助理解
协议解析与单线程 Reactor;它没有
redisObject 多态编码、也没有 RDB
fork——那是本系列第 3 篇起的对象层。
三、学术谱系与争论:cache 还是 data structure server?
3.1 奠基:Web 缓存与大规模 Memcache
| 工作 | 出处 | 本文引用点 |
|---|---|---|
| Nishtala et al., Scaling Memcache at Facebook | NSDI 2013(B 级) | 纯缓存、客户端分片、与 DB 的一致性实践;不假设服务端 rich type |
| Ousterhout et al., The Case for RAMCloud | HotOS 2009 / 后续 RAMCloud 论文 | 「内存作 primary store」愿景;与「Redis 作 cache」对照阅读 |
NSDI’13 的叙事是:Memcached 是 MySQL 前的加速层,失效与回填由应用与运维流程保证;论文 Figure 与 workload 针对 简单 get/set、高扇出,不是 general-purpose data structure server。
3.2 Redis 分叉:数据结构服务器
Salvatore Sanfilippo(antirez)在 Redis 早期公开说明中把项目定位为 data structure server(官方站点与源码 README,A/B 级):在内存里提供 语义化命令(列表、集合、有序集合),而不是仅缓存序列化 blob。持久化(RDB/AOF)与复制随后加入,使 Redis 在工程上 模糊「缓存 vs 小型 primary store」边界——这正是长期争论来源。
3.3 可核对的争论轴
| 立场 | 代表依据 | 主张 |
|---|---|---|
| A:Redis 应 primarily 作 cache | NSDI’13 实践;architecture/17 旁路模式 | 数据源在 DB;Redis 丢数据可重建;应用负责 invalidation |
| B:Redis 可作 durable in-memory store | Redis Persistence / Replication 官方文档;AOF everysec 等配置 | 部分团队把 Redis 当 主存储或会话层;需接受复制滞后与 fsync 窗口(第 11 篇) |
工程间隙:论文与官方文档讨论的是 机制语义(例如异步复制下丢什么),不是「能不能当唯一数据源」的 universal 答案。把 Redis 当 primary store 时,必须显式承担 failover、备份、跨 key 事务边界(Cluster 多 key 限制见第 13 篇)——这与 Memcached 论文假设的「MySQL 为真源」不同。
3.4 开放问题(系列收束于第 16 篇)
- 多线程执行命令是否应成为 Redis 主线? IO threads 已卸载读写;命令仍单线程。社区 fork(如 KeyDB、Valkey)与模块生态对 并行突变 有不同取舍(第 2、16 篇)。
- 嵌入 LSM vs 网络 Redis:本地 RocksDB 与远程 Redis 在 延迟、运维、跨服务共享 上如何划界?见 rocksdb/16 嵌入生产 与本系列第 16 篇选型树。
四、本系列 16 篇地图
flowchart TD
A["01 全景"] --> B["02 事件循环"]
B --> C["03 对象/SDS"]
C --> D["04 Dict rehash"]
D --> E["05 紧凑编码"]
E --> F["06 ZSet/Stream"]
F --> G["07 TTL"]
G --> H["08 maxmemory"]
H --> I["09 RDB"]
I --> J["10 AOF"]
J --> K["11 混合持久化"]
K --> L["12 复制"]
L --> M["13 Cluster"]
M --> N["14 Memcached"]
N --> O["15 排障"]
O --> P["16 选型"]
| 部分 | 篇目 | 一句话价值 |
|---|---|---|
| 模型与执行 | 01–02 | 生态位 + ae / IO
threads 精确边界 |
| 内存结构 | 03–06 | redisObject、dict、listpack、skiplist/stream |
| 过期与内存 | 07–08 | TTL 采样、近似 LRU/LFU vs 真 LRU |
| 持久化 | 09–11 | RDB fork、AOF fsync、混合与崩溃语义 |
| 分布式与收束 | 12–16 | PSYNC、16384 slot、Memcached 对照、排障、选型 |
推荐阅读路径(详见 系列 index):
- 从 architecture/17 下沉:1 → 2 → 3 → 8 → 11 → 16
- 数据结构专项:3 → 4 → 5 → 6
- 持久化专项:9 → 10 → 11 → 15
- Cluster 运维:2 → 12 → 13 → 16
五、常见误解
- 「Redis = 更快的 Memcached」。差的不只是实现语言:Redis 有 类型系统、持久化、复制、Cluster slot;Memcached 是 多线程 + slab + opaque value。吞吐数字随 workload 变化,不能脱离 item 大小、pipeline、持久化是否开启(本篇不列未实测排名)。
- 「用 HashMap 就不用 Redis」。HashMap 不能解决 跨进程/跨主机共享 与 独立扩缩容;architecture/17 的多级缓存里 L1 与 L2 是互补关系。
- 「Redis 和 RocksDB 都是 KV,可互换」。Redis 是 网络内存结构服务;RocksDB 是 进程内磁盘 LSM。Flink state 用 RocksDB 不是因为「Redis 不够快」,而是因为 本地耐久 + iterator + compaction 模型不同。
- 「读完 linux/redis-source 就等于掌握生产 Redis」。4.0 Org 笔记 valuable,但 7.4/8.x 在编码(listpack)、IO threads、客户端内存 eviction 等处已演进;应以本系列 + 当前 tag 源码为准。
- 「缓存服务器 = 淘汰算法」。TTL(第 7
篇)与
maxmemory淘汰(第 8 篇)是 不同路径;NSDI’13 的 Memcached 甚至以 LRU 类策略 + 手动失效 为主,不能套用 Redis 的 LFU 语义。
六、小结
三句话小结
- 进程内 HashMap、网络 Memcached/Redis、嵌入 RocksDB 解决的是不同边界上的 KV 问题:共享范围、值语义、持久化与扩缩容不可混谈。
- 本系列补的是 Redis 7.4/8.x 内核路径(事件循环 → 对象编码 → 淘汰 → RDB/AOF → 复制/Cluster),与 architecture/17、storage/66、4.0 redis-source 分工阅读。
- 「cache vs data structure server」 在文献与工程上并存:NSDI’13 代表纯缓存栈;Redis 官方定位与持久化能力代表另一分叉——选型需显式写出 真源在哪、丢数据是否可接受。
下一篇进入执行路径:事件循环与
I/O,用 ae.c / networking.c /
server.c 钉住「单线程」的精确含义。
参考资料
核心论文 / 系统文献
- Nishtala, Rajesh et al., Scaling Memcache at Facebook, NSDI 2013(大规模纯缓存实践;B 级,对读 architecture/17)。
- O’Neil, Patrick E. et al., The Log-Structured Merge-Tree (LSM-Tree), Acta Informatica, 1996(嵌入 KV 谱系;见 rocksdb/01)。
- Pugh, William, Skip Lists: A Probabilistic Alternative to Balanced Trees, CACM 1990(第 6 篇 ZSet 对照)。
规范 / 源码 / 官方文档
- Redis Documentation, Introduction / Persistence / Replication(redis.io,Redis OSS 7.4 / 8.x)。
- Redis OSS
源码:
src/server.c、src/object.c、src/dict.c(版本 tag 与正文一致)。 - Memcached 源码与
Wiki(
slabs.c、协议章节)。
站内
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【Redis / 缓存内核】事件循环与 I/O:ae · 客户端缓冲 · IO threads
按 Redis OSS 7.4/8.x 源码拆解 aeEventLoop 文件/时间事件、beforeSleep 钩子与客户端 querybuf/reply 路径,并精确说明 IO threads 只卸载读写与解析、命令仍在主线程执行。
【Redis / 缓存内核】事件循环 · 对象编码 · 淘汰 · RDB/AOF · Cluster
补齐内存 KV / 缓存服务器内核层:从 ae 事件循环与 redisObject 多态编码,到 TTL/maxmemory 近似淘汰、RDB/AOF 持久化语义,再到复制、Cluster hash slot 与 Memcached 对照。
【Redis / 缓存内核】对象模型与 SDS:redisObject · type · encoding
拆解 Redis 7.4/8.x 的 redisObject 多态层与 SDS 二进制安全字符串:type/encoding/refcount 如何分工、字符串四种编码的升级路径、OBJECT ENCODING 能读到什么,以及紧凑表示与统一抽象之间的工程争论。
【Redis / 缓存内核】Dict 与渐进 rehash:双 ht 与摊销扩容
从 Redis 7.4/8.x 的 dict 源码拆解链式哈希、双表渐进 rehash、rehashidx 步进与 DICT_RESIZE_AVOID 在 fork 期的行为,并对照站内哈希表内部文章说明与教科书一次性扩容的差异。