缓存与多级缓冲

返回 高并发

高并发下的缓存不仅是「加快读」,还包括写合并、读副本、热 key 治理、与 MQ 协同。通用三级缓存见 缓存架构;本文侧重极端 QPS 下的策略。


读路径:多级缓冲

请求
  → L1 Caffeine(进程内,μs)
  → L2 Redis(ms,跨实例)
  → L3 DB / 回源计算
数据L1L2说明
好友列表变更少,TTL 长
当日步数可选写频高,L1 短 TTL
Top100 全服榜极热,1~3s 刷新
用户资料与榜分离
LoadingCache<String, FriendList> friends = Caffeine.newBuilder()
    .maximumSize(50_000)
    .expireAfterWrite(5, MINUTES)
    .build(uid -> redis.get("friends:" + uid));

失效:关系链变更时 Pub/Sub 广播 invalidate(uid),防多节点 L1 不一致。


写路径:缓存作为「写缓冲」

高并发写不要每次都落 DB:

模式流程
Write-Behind先写 Redis,异步批量刷 DB
合并写内存攒 N 条或 T 秒,一次 Pipeline
计数型INCR / ZINCRBY,天然原子

排行榜步数:Redis 为真实时源,MySQL 为冷备份


热 key 与 Local Cache

热 key 问题

单 key QPS 超过单 Redis 节点上限(约 10 万级,视命令而定):

手段说明
本地缓存全服 Top100 每实例缓存 1s
随机分片key:{shard} 写分散,读聚合
读写副本读从 Redis 副本(注意复制延迟)
拆分全服榜改地区榜

热 key 探测

redis-cli --hotkeys
# 或 Monitor + 分析,业务埋点 key QPS

三大问题(高并发视角)

详见 Redis,高并发补充:

问题高并发场景
穿透无效 userId 批量探测;布隆 + 空值
击穿0 点榜单 key 新建 + 洪峰;互斥加载或逻辑过期
雪崩大量 key 同秒过期;TTL 抖动 + 多级降级

逻辑过期(排行榜常用):value 内嵌 expireAt,物理 key 不过期,后台线程异步重建。


Cache Aside 在高并发写的注意点

推荐:更新 DB 后删缓存(或延迟双删)
高并发写榜:Often 只写 Redis,DB 异步 —— 不适用经典双删,需对账
场景策略
步数上报只写 Redis + MQ,DB 滞后
资料修改删 Redis + 广播 L1 失效

Pipeline 与 Lua

批量读好友步数:

redisTemplate.executePipelined((RedisCallback<Object>) conn -> {
    for (String fid : friendIds) {
        conn.stringCommands().get(("step:20240627:" + fid).getBytes());
    }
    return null;
});

合并写用 Lua 保证 compare-and-set 步数。


与缓冲层协作

上报 → MQ(缓冲)→ 消费者写 Redis(内存缓冲)
读榜 → L1 → Redis
T+1 → Redis 快照 → DB

相关