Elasticsearch 搜索建议缓存系统 设计方案

在高并发搜索场景中,搜索建议(Suggester) 是用户搜索体验的核心功能。但由于 completion suggester 仍需访问 Lucene FST 结构,高频前缀(如 “a”、“ap”)的查询仍可能成为性能瓶颈。

为此,我们设计一套 Elasticsearch 搜索建议缓存系统,通过 多级缓存 + 预加载 + 智能失效 机制,实现 毫秒级响应、高吞吐、低延迟 的搜索建议服务。


一、目标与核心需求

目标说明
低延迟P99 < 10ms
📈 高吞吐支持每秒数万 QPS
🧱 可扩展支持多租户、多业务线
🔄 实时性新增商品/内容 1 分钟内可被建议
💾 节省资源减少对 Elasticsearch 的高频查询压力
🛡️ 高可用缓存失效时可降级回 ES

二、系统架构设计

       +------------------+
       | 用户输入 (前端)   |
       +--------+---------+
                |
                v
       +------------------+
       |  API 网关 / 网关缓存 | ← 可选:CDN 或边缘缓存
       +--------+---------+
                |
                v
       +------------------+
       |  搜索建议服务      |
       |  (Suggestion Service) |
       +--------+---------+
                |
   +-----+------+-----------------+
   |            |                 |
   v            v                 v
+--------+ +-------------+ +------------------+
| Redis  | | 本地缓存     | | Elasticsearch    |
| (L1)   | | (Caffeine)  | | (兜底 & 预加载)   |
+--------+ +-------------+ +------------------+
   |            |               |
   +------------+---------------+
                |
                v
         +------------------+
         |  数据更新系统    |
         |  (Kafka + Flink) |
         +------------------+

✅ 采用 多级缓存架构:本地缓存 → Redis → ES


三、缓存层级设计

1. L1:本地缓存(Caffeine / Guava)

  • 作用:极致低延迟,避免网络开销;
  • 容量:小(10~100MB),存储最热前缀;
  • TTL:60s;
  • 数据结构Map<String prefix, List<Suggestion>>
Cache<String, List<Suggestion>> localCache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(60, TimeUnit.SECONDS)
    .build();

✅ 适用于:a, ap, iph 等超高频前缀。


2. L2:Redis 集群(分布式缓存)

  • 作用:共享缓存,支持多实例;
  • 容量:大(GB 级),存储常见前缀;
  • TTL:300s;
  • 数据结构
    • STRING: sug:prefix:iph → JSON 列表
    • ZSET: sug:hot 存储热门词用于预加载
SET sug:prefix:iph '["iPhone", "iPhone 15", "苹果手机"]' EX 300

3. L3:Elasticsearch(兜底 & 预加载源)

  • 当缓存未命中时,回源查询 ES;
  • 用于预加载热门前缀到 Redis。
"suggest": {
  "text": "iph",
  "completion": {
    "field": "suggest_pinyin"
  }
}

四、缓存 Key 设计

场景Key 模板示例
普通前缀sug:prefix:{text}sug:prefix:iph
带业务类型sug:prefix:{type}:{text}sug:prefix:product:iph
带用户画像sug:prefix:{user_seg}:{text}sug:prefix:vip:iph
热门词集合sug:hotZADD sug:hot 100 "iph"

✅ 支持多维度缓存隔离。


五、缓存更新策略

1. 写时更新(Write-through)

当新增商品或内容时,触发缓存更新:

void onProductCreated(Product product) {
    List<String> inputs = generatePinyinAndChineseInputs(product.getTitle());
    for (String input : inputs) {
        String prefix = input.substring(0, Math.min(3, input.length())); // 前 3 字符
        redisClient.zincrby("sug:hot", 1.0, prefix); // 提升热度
    }
}

通过 Kafka 发送事件到建议服务。


2. 预加载(Pre-warming)

定时将热门前缀预加载到 Redis:

// 每 5 分钟执行
List<String> hotPrefixes = redis.zrevrange("sug:hot", 0, 99);
for (String prefix : hotPrefixes) {
    List<Suggestion> suggestions = esSuggester.suggest(prefix);
    redis.setex("sug:prefix:" + prefix, 300, serialize(suggestions));
}

使用 Flink 实时计算热门前缀。


3. 失效策略

场景失效方式
商品下架主动删除相关前缀缓存
商品改名删除旧前缀,添加新前缀
缓存过期TTL 自动失效
批量更新清空 Redis 缓存,触发预加载

六、查询流程设计

用户输入 'iph'
本地缓存命中?
返回结果
Redis 缓存命中?
写入本地缓存
查询 Elasticsearch
写入 Redis + 本地缓存

代码示例(Java)

public List<Suggestion> suggest(String prefix) {
    // 1. 本地缓存
    List<Suggestion> result = localCache.getIfPresent(prefix);
    if (result != null) return result;

    // 2. Redis
    result = redis.get("sug:prefix:" + prefix);
    if (result != null) {
        localCache.put(prefix, result);
        return result;
    }

    // 3. 回源 ES
    result = esSuggester.suggest(prefix);
    
    // 4. 写入缓存
    if (result != null && !result.isEmpty()) {
        redis.setex("sug:prefix:" + prefix, 300, result);
        localCache.put(prefix, result);
    }

    return result;
}

七、降级与容错

故障降级策略
Redis 不可用降级到本地缓存 + 直连 ES
ES 不可用返回空列表或缓存数据(短 TTL)
本地缓存 OOM自动驱逐,依赖 Redis
预加载失败继续服务,依赖实时查询

✅ 设置熔断器(如 Hystrix)防止雪崩。


八、性能优化建议 ✅

场景建议
前缀截断只缓存前 3~4 字符,减少 key 数量
批量查询支持 mget 批量获取多个前缀
压缩存储Redis 中使用 Snappy/GZIP 压缩
异步写入缓存更新异步化,避免阻塞主流程
热点探测实时监控高频前缀,动态预加载

九、监控与告警

1. 关键指标

指标说明
cache.hit_rate本地 + Redis 总命中率
latency.p99建议查询延迟
redis.memory.usedRedis 内存使用
es.suggest.qps回源 ES 的 QPS(越低越好)

2. 告警规则

  • 缓存命中率 < 90%
  • P99 延迟 > 50ms
  • Redis 内存 > 85%

十、扩展建议

场景建议方案
个性化建议基于用户画像返回定制化建议
A/B 测试不同用户组返回不同排序
冷启动问题初始加载运营配置的热门词
多语言支持按语言分缓存 namespace
边缘缓存CDN 缓存静态建议(如首页推荐)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值