2026年7月13日 · 6 分钟阅读
缓存架构与选型:何时使用缓存、Redis 与 Memcached 如何选择
从 Cache-Aside 读写流程出发,分析缓存收益与成本、本地缓存和分布式缓存的边界,并对比 Redis 与 Memcached 的适用场景。
缓存是用空间换时间:把访问频繁、计算昂贵的数据副本放到更快的介质,减少数据库和下游服务压力。它不是“加一层 Redis 就会变快”,而是一套围绕命中率、过期、一致性和故障降级的架构决策。
除非业务明确把 Redis 设计成主存储,否则缓存只是可重建的派生副本,数据库或事件日志才是事实来源。
什么数据值得缓存
适合缓存的数据通常同时满足:读多写少、访问有热点、允许短暂陈旧、获取或计算成本高。商品详情、配置、权限结果和聚合统计都很典型。
不适合盲目缓存的情况包括:每次请求几乎都不同、数据更新频率接近读取频率、必须读取最新值、数据量很小且数据库本身足够快。低命中率缓存会额外增加网络、序列化和运维成本。
缓存收益可粗略表示为:
平均读取耗时 ≈ 命中率 × 缓存耗时 + 未命中率 ×(缓存耗时 + 数据源耗时)
因此要关注命中率、加载成本和尾延迟,而不是只看 Redis 的单次响应时间。
Cache-Aside:最常见的缓存模式
Cache-Aside 由应用管理缓存,是业务系统最常见的模式。
读取流程:
读缓存 → 命中则返回
→ 未命中则读数据库 → 写缓存并设置 TTL → 返回
更新流程通常是:
更新数据库 → 删除缓存
选择删除而不是直接更新缓存,原因是缓存值可能由多张表或复杂计算生成;删除后由下一次读取按最新事实重建,更容易收敛。这个模式提供的是带有时间窗口的最终一致性,而不是跨数据库和 Redis 的原子一致。
其他缓存模式
| 模式 | 谁负责读写 | 优点 | 代价与场景 |
|---|---|---|---|
| Cache-Aside | 应用 | 灵活,只缓存实际访问数据 | 业务代码负责一致性和回源保护 |
| Read-Through | 缓存层 | 应用只面对缓存接口 | 需要统一加载器和抽象层 |
| Write-Through | 写缓存时同步写数据源 | 写后容易读到一致结果 | 写延迟增加,缓存层成为关键路径 |
| Write-Behind | 先写缓存,异步批量落库 | 写吞吐高 | 数据丢失、顺序、重试和恢复更复杂 |
业务数据库前的通用缓存通常优先 Cache-Aside。Write-Behind 只有在可以接受异步持久化,并具备日志、重放和对账能力时才考虑。
本地缓存与分布式缓存
| 维度 | 本地缓存 | Redis 等分布式缓存 |
|---|---|---|
| 延迟 | 进程内访问,最低 | 有网络和序列化开销 |
| 容量 | 受单实例内存限制 | 可集中管理和分片扩展 |
| 一致性 | 多实例副本难同步 | 所有实例访问统一数据 |
| 故障影响 | 单实例失效 | 缓存集群可能影响整个服务 |
| 适用数据 | 小型、稳定、超热点数据 | 共享业务数据和大工作集 |
二级缓存可以先查本地再查 Redis,但失效链路也多了一层。可使用短 TTL、消息通知或 Redis client-side caching 的失效通知;一旦失效通知连接中断,应清空本地缓存,避免长期陈旧。
Redis 与 Memcached 如何选择
| 维度 | Redis | Memcached |
|---|---|---|
| 数据结构 | String、Hash、List、Set、ZSet、Stream 等 | 以简单 key-value 为主 |
| 持久化与复制 | 提供 RDB/AOF、复制、哨兵和 Cluster | 核心定位是易失缓存,能力更简单 |
| 原子能力 | 丰富命令、事务、WATCH、Lua | 计数和 CAS 等基础操作 |
| 数据治理 | TTL、淘汰策略、丰富观测工具 | 简洁、易水平拆分 |
| 适用方向 | 缓存之外还可承载计数、排行、会话和协调 | 纯对象缓存、需求简单的存量系统 |
今天的新系统多数会优先评估 Redis,因为生态和数据结构更丰富;但这不是说 Memcached 没有价值。如果系统只需要简单、易失的对象缓存,团队已有稳定运维体系,Memcached 的简单性反而是优势。
不要因为 Redis 支持持久化就把它默认当数据库。内存成本、淘汰、复制延迟和数据模型仍决定它是否适合作为事实来源。
Key 与数据模型设计
推荐采用可读、可版本化的命名:
cache:product:v2:10086
cache:user-profile:v1:42
设计时注意:
- key 包含业务域、实体、版本和标识,避免不同服务冲突。
- value 不要无限增长,大对象应拆分或改用对象存储。
- TTL 根据业务陈旧容忍度设置,并加入随机抖动防止集中失效。
- 不把敏感信息无保护写入缓存;权限、网络和传输加密同样重要。
- schema 变更优先切换版本前缀,旧缓存自然过期。
容量与命中率
上线前估算工作集,而不是全量数据:
内存 ≈ 热点 key 数 ×(key + value + 对象开销)× 安全系数
还要监控命中率、eviction、内存碎片、网络流量和回源 QPS。高命中率不必然健康:如果缓存命中的都是小流量数据,而一个热点 key 持续压垮单分片,整体命中率仍可能很好看。
工程落地清单
- 明确事实来源和允许陈旧的最长时间。
- 通过真实访问分布估算命中率、工作集和峰值 QPS。
- 为缓存未命中、Redis 超时和全量不可用设计数据库保护。
- 为热点失效加入单飞、互斥重建或逻辑过期。
- 为缓存 key、value schema、TTL 和序列化格式建立规范。
- 压测正常、冷启动、热点失效和 Redis 故障四种路径。
- 监控命中率、回源量、最慢命令、内存和淘汰。
面试回答要点
回答“为什么使用缓存”时,先说业务的读写比和数据库瓶颈,再说明缓存通过高命中率减少数据源访问。随后主动指出缓存带来一致性、雪崩、穿透、击穿和运维问题。选型时从数据结构、持久化高可用、原子能力、容量和团队经验对比 Redis 与 Memcached,而不是只背“Redis 更快”。
总结
缓存的核心不是速度,而是管理一份允许短暂陈旧的派生副本。先确定数据是否值得缓存和事实来源,再选择 Cache-Aside 等模式,最后通过 TTL、容量、回源保护和监控把失败路径补齐。