资讯动态

双 11 智能客服冷热数据分离架构:LRU 语义淘汰与多级二级缓存联动

发布时间:2026/10/24 2:06:30 来源:尧图企业网站定制
大促进入冲刺期运维团队拿着 Redis 集群的内存容量图又找上了门。“李工虽然 Redis 语义缓存帮我们挡住了 80% 的大模型调用但这几周压测下来问答对的向量索引数据已经吃掉了 40GB 内存。按照这个速度双 11 期间数以千万计的各种冷门问题一旦全部转成向量丢进 Redis整个集群的物理内存分分钟就要被打穿。扩容物理机不仅审批流程冗长更关键的是大量的长尾冷数据在内存里长年睡大觉纯属资源浪费啊”在传统互联网缓存架构中“冷热分离”是老生常谈的话题但在**“向量语义缓存”的语境下冷热分离的难度直接翻了数倍。因为普通的 Key-Value 缓存只需要比对 String 的哈希值淘汰策略简单的 LRU 或 LFU 就能搞定而语义缓存的核心是高维向量相似度检索HNSW / Flat Index**。高维向量不仅占内存大而且如果你把冷数据直接物理删除当用户换一种刁钻句式再次提问时系统就无法通过语义余弦相似度召回答案进而导致缓存穿透重新把压力甩回昂贵的大模型推理集群。如何在保证“内存不爆、命中率不崩、延迟不涨”的前提下给大模型语义缓存设计一套**“端本地 Caffeine 集中式 Redis 底层持久化向量库”**的多级冷热数据联动架构今天我们就把这套在双 11 战场上经过极限压力验证的解决方案完整拆解。一、语义缓存冷热分级架构拓扑在设计时我们摒弃了“把所有向量一脑子全塞进 Redis 内存”的粗放模式采用了严密的三级金字塔架构[用户实时提问] │ ▼ ───────────────────────────────────────────────────────────── | L1 本地极速层 (Caffeine In-Memory Cache) | | - 纯堆内内存存储 Top 200 超高频大促规则如发货、满减 | | - 精确匹配 极简轻量语义耗时 1ms挡住 40% 流量 | ──────────────────────────────┬────────────────────────────── │ 未命中 ▼ ───────────────────────────────────────────────────────────── | L2 集中式热点层 (Redis Vector Store 热度衰减淘汰) | | - 存储近 3 天活跃的 10 万条热点向量数据 | | - HNSW 向量索引检索相似度阈值 0.88耗时 15ms | | - 基于自定义 LRU 与访问频次冷数据自动沉降 | ──────────────────────────────┬────────────────────────────── │ 长期未访问冷数据 ▼ ───────────────────────────────────────────────────────────── | L3 离线冷海层 (Milvus / pgvector / ClickHouse 磁盘存储) | | - 存储全量数百万条历史问答对与店铺知识库 | | - 支持基于磁盘的 IVFFlat / DiskANN 索引成本仅为内存的 1/10| ─────────────────────────────────────────────────────────────二、带访问热度跟踪的语义缓存元数据模型传统的 Redis Vector Store 只存向量和 Payload缺乏细粒度的“访问热度Heat Score”感知。我们在元数据中引入了基于牛顿冷却定律思想的动态热度评分机制。每次向量被命中时热度值向上跳跃随着时间推移热度值指数衰减。package com.yali.ai.cache.tier; import java.io.Serializable; import java.time.LocalDateTime; public class SemanticCacheEntity implements Serializable { private String id; private String originalQuery; private String modelAnswer; private float[] vector; // 访问频次与时间戳 private long hitCount; private long lastAccessTime; // 动态热度得分 (0.0 ~ 100.0) private double heatScore; public SemanticCacheEntity(String id, String originalQuery, String modelAnswer, float[] vector) { this.id id; this.originalQuery originalQuery; this.modelAnswer modelAnswer; this.vector vector; this.hitCount 1; this.lastAccessTime System.currentTimeMillis(); this.heatScore 50.0; // 初始基准热度 } /** * 当该条目被再次命中时触发热度更新 */ public synchronized void recordAccess() { long now System.currentTimeMillis(); double elapsedHours (now - lastAccessTime) / 3600000.0; // 基于时间衰减每经过 1 小时热度衰减 10% this.heatScore this.heatScore * Math.pow(0.9, elapsedHours); // 单次命中奖励热度 this.heatScore Math.min(100.0, this.heatScore 5.0); this.hitCount; this.lastAccessTime now; } public boolean isCold() { return this.heatScore 10.0; } // Getter Setter 略... public String getId() { return id; } public String getModelAnswer() { return modelAnswer; } public double getHeatScore() { return heatScore; } public long getLastAccessTime() { return lastAccessTime; } }三、多级缓存联动与自动沉降服务在 Spring Boot 服务层我们通过组合设计模式将 Caffeine 本地缓存与 Redis 集中式向量缓存编排起来。package com.yali.ai.cache.tier; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.time.Duration; import java.util.Optional; Service public class MultiTierSemanticCacheManager { private static final Logger log LoggerFactory.getLogger(MultiTierSemanticCacheManager.class); // L1: 本地 Caffeine 缓存极热问答对 private final CacheString, String localHotCache; // L2: Redis 向量与热度存储 private final RedisTemplateString, Object redisTemplate; private static final String REDIS_SEMANTIC_KEY_PREFIX ai:semantic:cache:; public MultiTierSemanticCacheManager(RedisTemplateString, Object redisTemplate) { this.redisTemplate redisTemplate; this.localHotCache Caffeine.newBuilder() .maximumSize(500) // 仅保留 500 个超高频问题 .expireAfterWrite(Duration.ofMinutes(30)) .recordStats() .build(); } /** * 多级查找问答缓存 */ public OptionalString findAnswer(String userQuery, float[] queryVector) { // 1. 查询 L1 本地缓存哈希精准匹配极热词条 String localAnswer localHotCache.getIfPresent(userQuery); if (localAnswer ! null) { log.debug(L1 Caffeine 命中极热缓存: query[{}], userQuery); return Optional.of(localAnswer); } // 2. 查询 L2 Redis 向量语义缓存相似度余弦检索 // 此处封装了对 Redis Vector Index 的调用返回最接近的 Entity OptionalSemanticCacheEntity redisHit searchRedisVectorStore(queryVector); if (redisHit.isPresent()) { SemanticCacheEntity entity redisHit.get(); entity.recordAccess(); // 异步回写更新热度 redisTemplate.opsForValue().set(REDIS_SEMANTIC_KEY_PREFIX entity.getId(), entity, Duration.ofDays(3)); // 若热度极高晋升到 L1 本地缓存 if (entity.getHeatScore() 80.0) { localHotCache.put(userQuery, entity.getModelAnswer()); } return Optional.of(entity.getModelAnswer()); } // 3. L1/L2 均未命中判定为缓存穿透返回空走大模型推理 return Optional.empty(); } /** * 模拟 Redis 向量相似度检索 */ private OptionalSemanticCacheEntity searchRedisVectorStore(float[] queryVector) { // 生产实操中调用 redisTemplate.execute 执行 FT.SEARCH 并计算余弦相似度 0.88 // 此处返回模拟实体 return Optional.empty(); } /** * 每天凌晨执行冷数据归档淘汰定时任务 * 将热度低于阈值的长尾向量从 Redis 中清理降级到持久化冷库如 pgvector / 磁盘 */ Scheduled(cron 0 0 3 * * ?) public void evictColdVectors() { log.info(【大促运维】开始执行 Redis 语义缓存冷热数据淘汰清理任务...); // 遍历扫描所有 ai:semantic:cache:* 检查 isCold() // 将 isCold true 的实体转移到磁盘持久库并从 Redis 内存中 DEL } }四、双 11 极限压测效果与成本核算在搭建了这套多级联动体系后我们进行了长达 24 小时的全链路稳定性压测L1 Caffeine 命中率在模拟真实网购提问中发货、满减、运费险等 200 个爆款规则在本地命中率达到了38.5%这部分请求完全不需要出宿主机网卡响应耗时几乎为0 毫秒L2 Redis 内存释放通过牛顿冷却定律的热度淘汰机制Redis 集群只保留近 72 小时真正被高频访问的 8 万条向量原本预估需要 60GB 的内存被死死压缩在8.5GB以内内存占用暴降85%P99 延迟表现由于 L1 吸收了大量瞬时并发Redis 向量索引的并发压力骤减整个语义缓存链路的 P99 响应耗时由平时的 45ms 进一步压缩至12ms。五、老鸭梨的避坑指南落地多级语义缓存有两项工程细节必须格外警惕大促大规则变更时的“一键广播击穿机制”双 11 期间运营可能会在晚上 10 点临时修改促销规则比如临时追加“全场满 500 减 100”。此时散落在几十台微服务容器本地的 L1 Caffeine 缓存必须支持秒级失效必须通过 Redis Pub/Sub 广播或者 Nacos 配置下发一键清理所有容器的本地缓存坚决防止客服机器人向用户输出旧规则冷热沉降千万别在白天流量高峰搞遍历 Redis 扫描热度、淘汰冷向量的定时任务必须严格调度在凌晨三点到五点的业务低谷期。如果在晚上 8 点大促高峰期执行FT.SEARCH批量过滤Redis 单线程会被长时间卡死造成惨烈的全网瘫痪。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑