Skip to content

缓存雪崩 缓存击穿 热key大key 持久化尽量避免丢失数据 数据库redis一致性

下面给出一份「面试速记表 + 实战代码 + 口诀」,把 Redis 最常见的 5 类“大坑”一次性整理清楚。


口诀:
雪崩一大片,击穿一个点;
热 key 扛不住,大 key 拖全网;
持久化丢数据,缓存库不同步。


坑名触发场景典型现象防护 4 件套(按优先级)一句话记忆
1. 缓存雪崩大量 key 同时过期 / Redis 宕机所有请求直达 DB,DB 被打挂• 过期时间加随机值(散列)
• Redis Cluster + 哨兵高可用
• 本地 Caffeine 二级缓存
• 降级限流(Sentinel)
“把过期时间打散,别让千军万马一起冲”
2. 缓存击穿单个热点 key 失效瞬间该 key 的并发全部打到 DB• 热点 key 永不过期 + 异步刷新
• 互斥锁(SET NX PX + Lua)
• 逻辑过期(value 里存时间戳)
• 提前预热 + 布隆过滤器
“热点 key 永不过期,或加互斥锁”
3. 热 key访问 QPS 极高,单节点网卡占满Redis 单分片 CPU 100%• 本地缓存 + 一致性哈希分片
• 拆 key:将热 key 按 hash 后缀分成 N 份
• 读写分离(副本节点)
“把 1 把钥匙拆成 100 把”
4. 大 keyvalue 过大(>10 MB)慢查询、阻塞、同步延迟• 业务拆分:按日期/ID 分段
• 使用 Hash / List 分片
• 定期扫描:redis-cli --bigkeys
• 惰性删除:UNLINK 代替 DEL
“大 key 瘦身,拆、分、删”
5. 持久化丢数据RDB 快照间隔内崩溃最近写入全部蒸发• AOF 每秒刷盘 (appendfsync everysec)
• RDB + AOF 混合模式
• 双写日志:Binlog → Canal → Redis
• 异地多活
“RDB 做备份,AOF 做实时,双保险”

数据库 & 缓存一致性 3 种套路

  1. Cache-Aside(旁路缓存)
    读:先缓存 → 没有再查库 → 写回缓存
    写:先写库 → 再删缓存(延迟双删防并发)

    java
    @CacheEvict(key = "#id", beforeInvocation = false)
    public void updateOrder(Long id, OrderDTO dto) {
        orderMapper.updateById(id, dto);
        // 延迟 500 ms 再次删,防并发脏读
        executor.schedule(() -> redisTemplate.delete("order:"+id), 500, TimeUnit.MILLISECONDS);
    }
  2. 分布式锁 + 双写
    写请求先拿 Redisson 锁 → 写库 → 写缓存 → 解锁

  3. Canal / Debezium 异步订阅
    MySQL binlog → Canal → Kafka → 更新 Redis,零侵入,最终一致


热 key 互斥锁示例(Lua 原子脚本)

lua
-- KEYS[1] 锁名  ARGV[1] 过期毫秒
if redis.call('SET', KEYS[1], 1, 'NX', 'PX', ARGV[1]) then
    return 1   -- 拿到锁
else
    return 0   -- 没拿到
end

Java 封装

java
Boolean locked = redisTemplate.execute(
        RedisScript.of(lua, Boolean.class),
        Collections.singletonList("lock:sku:" + skuId),
        1000);
if (Boolean.TRUE.equals(locked)) {
    try {
        // 查询数据库并回写缓存
    } finally {
        redisTemplate.delete("lock:sku:" + skuId);
    }
} else {
    // 自旋 or 返回默认值
}

总结口诀再背一遍
雪崩—打散过期,击穿—锁/永不过期,热 key—拆分,大 key—瘦身,持久化—RDB+AOF,一致性—延迟双删 + 异步 binlog。