缓存:Redis vs Memcached

应用 → Cache(先查)→ DB(缓存没有再查)
维度 Redis Memcached
数据结构 字符串 / 列表 / 哈希 / 集合 / Sorted Set / Stream / Pub-Sub 仅字符串(key/value)
持久化 支持(AOF / RDB) 不支持
集群 Redis Cluster / Sentinel 客户端分片
内存效率
适合 多用途,"瑞士军刀" 纯 KV 缓存,规模超大

2026 主流是 Redis——除非你是特别大规模的纯 KV 场景,Memcached 不再是首选。

注:Redis 7.4 起改用 SSPL/RSALv2 双许可,社区分叉出 Valkey(Linux Foundation)和 KeyDB。新项目可以考虑 Valkey。

缓存使用模式

Cache-Aside(最常见)

def get_user(uid):
    val = cache.get(f"user:{uid}")
    if val:
        return val
    val = db.query(uid)
    cache.set(f"user:{uid}", val, ttl=300)
    return val

应用控制读写——简单灵活。

Write-Through

def update_user(uid, data):
    db.update(uid, data)
    cache.set(f"user:{uid}", data)    # 同时更新缓存

写时同步缓存——读永远新鲜,但写更慢。

Write-Behind / Cache Invalidation

更复杂,少用。

缓存的几个老坑

  • 缓存穿透:查不存在的 key → 永远落到 DB → DDoS。对策:缓存空结果(用短 TTL)
  • 缓存雪崩:大量 key 同时过期 → DB 一瞬间被打挂。对策:TTL 加随机抖动
  • 缓存击穿:热点 key 过期那一刻被多个请求同时打 DB。对策:互斥锁 / 异步重建
  • 数据不一致:DB 改了缓存没改 → 取到旧值。对策:写时 invalidate 缓存(不是 update)

消息队列:核心场景

生产者 → MQ → 消费者

什么时候真的需要 MQ:

  1. 解耦:生产者不等消费者,各自伸缩
  2. 削峰:流量瞬时爆 → 进队列慢慢消费
  3. 重试 / 异步:邮件 / 短信 / 长任务,失败可重试
  4. 事件驱动:一处发生变化,多处消费

主流选型

MQ 模型 主要特点
Apache Kafka 日志(pull) 高吞吐、长保留、分区、流处理生态
RabbitMQ 经典队列(AMQP) 灵活路由(exchange / binding)、低延迟
NATS / NATS JetStream 发布订阅 + 持久流 极轻量、低延迟、云原生
Redis Streams 流(pull) 已有 Redis 时附加功能
AWS SQS / SNS 托管队列 / Pub-Sub 完全托管,按量计费
GCP Pub/Sub Pub-Sub 全球发布
阿里 RocketMQ / 腾讯 CMQ / 华为 DMS 类 Kafka 国内同等场景
Apache Pulsar 分布式流 多租户、计算存储分离

决策矩阵(一种思路)

你的需求 推荐
简单异步任务(邮件 / 后台处理) Redis Queue / SQS / RabbitMQ
高吞吐数据流(日志 / 事件) Kafka
服务间事件总线(微服务事件驱动) NATS / Kafka / Redis Streams
路由复杂(按 key fan-out / 优先级队列) RabbitMQ
不想运维 + 不大不小规模 云商托管(SQS / GCP Pub/Sub)
已经有 Redis Redis Streams / Queue(先用,规模真大再上 Kafka)

Kafka 的常被低估的复杂度

Kafka 强大但运维负担重

  • 需要 ZooKeeper(旧版本)/ KRaft(新版)
  • 分区 + 复制因子要规划
  • 消费者位移管理
  • Schema Registry(Avro / Protobuf)
  • 监控、扩容、磁盘管理

没有专职团队前,用托管 Kafka(Confluent Cloud / AWS MSK / 阿里 / 腾讯)。

消息系统的几个原则

1. 至少一次 ≠ 一次

绝大多数 MQ 提供至少一次(at-least-once)——可能重复。消费者必须幂等

def process(msg):
    if already_processed(msg.id):
        return
    do_work(msg)
    mark_processed(msg.id)

2. 顺序仅在分区内保证

Kafka 同分区有序,跨分区无序。有序性靠分区 key 划分

3. Dead-letter Queue(DLQ)

反复处理失败的消息 → 转到 DLQ → 人工处理。没有 DLQ 的队列会被毒消息卡死

4. 监控滞后(Consumer Lag)

消费跟不上生产 → 队列堆积。Lag 必须有告警

反模式

  • MQ 当数据库用:MQ 是"事件流",不适合点查
  • 小项目就上 Kafka:3 服务上 Kafka = 杀鸡用牛刀 + 运维负担
  • 缓存当真相:DB 才是真相,缓存可以丢;反过来则灾难
  • 消息没幂等性,靠 ack:网络抖动 → 重复消费 → 数据错

推荐阅读

下一篇是这一系列的最后一篇——容量规划 + FinOps 入门