深入设计:一个高可用的本地 + 分布式缓存混合系统架构实践

共计 3154 个字符,预计需要花费 8 分钟才能阅读完成。

在高性能、高并发的系统架构中,缓存系统不仅是性能保障的核心,更是“服务稳定性”的关键支撑点。单一 Redis 已无法满足对低延迟和高可用性的极致追求,因此,“本地缓存 + 分布式缓存”的双层架构成为主流选择。

本文将带你系统地深入了解:

  • ✅ 双层缓存架构的核心设计
  • ✅ 高级特性与工程化封装
  • ✅ 易错陷阱与解决方案
  • ✅ 弹性与容错能力提升方案

一、混合缓存的本质价值

我们希望达成的目标:

维度 要求
性能 99% 请求纳秒级响应
一致性 缓存数据无显著滞后
稳定性 Redis 故障不影响核心功能
扩展性 可动态调整缓存策略与节点数

➡️ Redis 负责共享、本地缓存负责极速访问、两者组合形成多级缓存闭环。

二、混合缓存架构设计图

深入设计:一个高可用的本地 + 分布式缓存混合系统架构实践

三、完整源码封装

通用缓存访问逻辑

public <T> T get(String key, Class<T> clazz, Supplier<T> dbLoader) {
    // 1. 本地缓存
    Object local = localCache.getIfPresent(key);
    if (local != null) return clazz.cast(local);

    // 2. Redis
    String redis = redisTemplate.opsForValue().get(key);
    if (redis != null) {
        T val = JsonUtil.fromJson(redis, clazz);
        localCache.put(key, val);
        return val;
    }

    // 3. DB Fallback
    T val = dbLoader.get();
    if (val != null) {
        localCache.put(key, val);
        redisTemplate.opsForValue().set(key, JsonUtil.toJson(val), Duration.ofMinutes(10));
    }
    return val;
}

四、常见问题与基本解决方案

1. 多节点本地缓存一致性问题

问题场景:

A 节点写入 Redis 后,B 节点的本地缓存仍然是旧值,数据脏读

解决方案:使用 Redis 发布订阅机制通知其他节点清理本地缓存

// 写入时
redisTemplate.convertAndSend("cache:evict", key);

// 监听器
@EventListener
public void onMessage(Message message) {
    String key = message.toString();
    localCache.invalidate(key);
}

2. 缓存雪崩

问题场景:

大量缓存同时过期,全部打到数据库或 Redis 上

解决方案:设置缓存过期时间加上 随机扰动,避免同一时间集中过期

long ttl = 600 + ThreadLocalRandom.current().nextInt(60); // 600s ± 60s
redisTemplate.opsForValue().set(key, json, Duration.ofSeconds(ttl));

3. 缓存穿透

问题场景:

请求的是数据库不存在的数据,频繁请求打到底层数据库

解决方案:

  • 将 null 值也缓存(短 TTL)
  • 或使用 布隆过滤器 拦截非法 Key
if (dbVal == null) {
    redisTemplate.opsForValue().set(key, "NULL", Duration.ofSeconds(30));
    return null;
}

4. 缓存击穿(热点 Key 遭遇并发访问)

public <T> T getWithMutex(String key, Class<T> clazz, Supplier<T> dbLoader) {
    // 1. 尝试从缓存读取
    String cacheVal = redisTemplate.opsForValue().get(key);
    if (cacheVal != null) {
        return JsonUtil.fromJson(cacheVal, clazz);
    }

    // 2. 分布式锁防止击穿
    RLock lock = redissonClient.getLock("lock:cache:" + key);
    try {
        boolean locked = lock.tryLock(10, 5, TimeUnit.SECONDS); // 最多等10s,锁5s释放
        if (locked) {
            // double-check:再次检查缓存
            cacheVal = redisTemplate.opsForValue().get(key);
            if (cacheVal != null) {
                return JsonUtil.fromJson(cacheVal, clazz);
            }

            // 查询数据库并写入缓存
            T dbVal = dbLoader.get();
            if (dbVal != null) {
                redisTemplate.opsForValue().set(key, JsonUtil.toJson(dbVal), Duration.ofMinutes(10));
            } else {
                // null缓存防穿透
                redisTemplate.opsForValue().set(key, "NULL", Duration.ofSeconds(30));
            }
            return dbVal;
        } else {
            // 未拿到锁,可选择等待一小段时间后重试(或立即返回 null / fallback)
            Thread.sleep(50); // 简化策略
            return getWithMutex(key, clazz, dbLoader); // retry
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return null;
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

5. Redis与MySQL数据不一致

这里就不细说了,后续可能会开一篇文章详细讨论

  • 缓存更新采用双删 + 延迟删策略
  • 引入消息队列或 Canal 监听 DB binlog 同步更新

五、架构级别的扩展设计

1. 支持异步刷新(refresh-after-write)

// 配置 Caffeine 支持异步 refresh
Caffeine.newBuilder()
    .refreshAfterWrite(3, TimeUnit.MinUTES)
    .buildAsync(new AsyncCacheLoader<String, Object>() {
        public CompletableFuture<Object> asyncLoad(String key, Executor executor) {
            return CompletableFuture.supplyAsync(() -> remoteLoader(key), executor);
        }
    });

2. 支持统一缓存监控(命中率、TTL、命中层级)

  • 统计本地命中/Redis 命中/DB 查询次数
  • 将日志写入监控平台(如 Prometheus + Grafana)

3. 缓存注解化(自动识别缓存策略)

使用自定义注解 @HybridCache(key="user:{id}", ttl=600) 自动处理缓存读取逻辑。

六、实战经验小结

问题 推荐做法
本地缓存过期如何控制? 设置 TTL、refresh、监听 Redis
Redis 挂了怎么办? 降级到本地缓存(弱一致性兜底)
多线程高并发怎么处理? 加锁、预热、异步刷缓存
如何保证低延迟和一致性 异步刷新 + 通知同步 + TTL 折中

七、总结

本地缓存 + Redis 分布式缓存的混合架构,不再只是“加一层缓存”这么简单,它是一个具备:

  • 低延迟访问
  • 分布式一致性保障
  • 高可用容灾容错
  • 支持动态扩容与监控
  • 适配多业务数据模型

的综合解决方案。

✅ 适合所有对性能敏感的中大型业务系统,特别是在权限缓存、热点配置、组织树等场景。


我是 李卷卷,专注Java相关知识输出。感谢阅读!

正文完
 0
admin
版权声明:本站原创文章,由 admin 于2025-06-03发表,共计3154字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)