缓存与消息队列
后端系统里,缓存用来扛读压、降延迟;消息队列用来削峰、解耦、异步化。二者常一起出现,但解决的问题不同。数据建模与索引见 数据库。
缓存解决什么
| 痛点 | 缓存怎么帮 |
|---|---|
| 热点读打穿 DB | 内存命中,毫秒级响应 |
| 重复计算 / 聚合贵 | 缓存结果或中间态 |
| 跨服务重复拉同一份数据 | 共享缓存层(Redis 等) |
一句话:用空间换时间,用最终一致换可用性——要接受缓存与 DB 可能短暂不一致。
常见缓存模式
| 模式 | 流程 | 注意 |
|---|---|---|
| Cache Aside | 读:cache → miss → DB → 写 cache;写:更新 DB → 删 cache | 最常用;删缓存时机与并发 |
| Read Through | 应用只读 cache,miss 由 cache 组件回源 | 组件要支持 |
| Write Through | 写同时更新 cache 与 DB | 写延迟略高 |
| Write Behind | 先写 cache,异步刷 DB | 丢数据风险,慎用 |
Redis 要点
Redis 是缓存面试最高频的中间件之一:数据结构、持久化、集群、分布式锁、穿透击穿雪崩 等见 Redis 专篇。
热 key 问题 → 本地缓存 / 多副本 / 拆分 key
缓存穿透 → 布隆过滤器 / 空值缓存
缓存击穿 → 互斥重建 / 逻辑过期
缓存雪崩 → TTL 随机抖动 / 多级缓存 / [限流降级](/docs/backend/限流熔断与降级)
消息队列解决什么
| 痛点 | MQ 怎么帮 |
|---|---|
| 峰值流量 | 队列缓冲,消费者按能力拉 |
| 强耦合调用链 | 发布方只发事件,订阅方独立演进 |
| 耗时任务 | 异步化(发邮件、对账、索引构建) |
| 跨系统最终一致 | 事件驱动 + 补偿 |
核心概念
| 概念 | 说明 |
|---|---|
| Producer / Consumer | 发消息 / 收消息 |
| Topic / Queue | 主题广播 vs 队列竞争消费 |
| Partition | 分区内有序;提高并行度 |
| Consumer Group | 同组内一条消息只被一个消费者处理 |
| ACK | 消费确认;失败重试或进死信 |
| At-most-once / At-least-once / Exactly-once | 语义与幂等设计 |
选型直觉(不绑死产品)
| 产品族 | 特点 | 典型场景 |
|---|---|---|
| Kafka | 高吞吐、日志型、分区有序 | 日志、埋点、流式管道 |
| RabbitMQ | 路由灵活、协议成熟 | 业务事件、任务队列 |
| RocketMQ | 事务消息、定时、顺序 | 国内电商、金融常见 |
面试答法:先讲语义(顺序、重复、延迟),再讲产品。Kafka 细节见 Kafka。
缓存 vs MQ:别混用
| 缓存 | 消息队列 | |
|---|---|---|
| 主要目的 | 加速读 | 异步、解耦、削峰 |
| 数据生命周期 | 可丢、可过期 | 通常需持久化与可追溯 |
| 一致性 | 与 DB 最终一致 | 消费幂等、重复投递 |
| 误用 | 把 MQ 当缓存堆消息 | 用 Redis List 当严肃 MQ 不设持久与 ACK |
与数据库联动
| 场景 | 常见做法 |
|---|---|
| 写 DB 后发事件 | 本地消息表 / 事务发件箱(Outbox) |
| 消费端更新 DB | 幂等键(业务 id)、乐观锁 |
| 读自己的写 | 关键路径读主库或带版本校验 |
常见误解
- 「Redis 就是缓存」——也可做分布式锁、计数、简单队列,但角色要分清
- 「MQ 保证不丢就行」——还要幂等、顺序、积压监控、死信处理
- 「Exactly-once 开箱即用」——多数系统实际是 at-least-once + 幂等消费
- 「缓存能替代 DB」——缓存是加速层,不是唯一真相源