共计 2036 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:为什么传统 find skill 会失效
在用户量突破千万级、日活百万的业务场景中,我们发现传统 find skill 实现存在三大致命伤:

- 查询延迟飙升:当数据量超过 MySQL 单表 2000 万行时,即使有基础索引,模糊查询响应时间仍从 200ms 劣化到 2s+,直接导致接口超时
- 数据库不堪重负 :一个简单的
LIKE '%keyword%'查询可能触发全表扫描,某次运营活动期间单个 find skill 接口就占用了 80% 的数据库 CPU - 维护成本激增:业务规则变更需要修改数十处 SQL 语句,缺乏统一处理逻辑
技术选型:鱼与熊掌如何兼得
我们对比了三种主流方案:
- 纯内存缓存方案
- 优点:Redis 查询仅需 1ms,吞吐量可达 10w QPS
-
缺点:缓存穿透风险,且无法支持复杂条件组合查询
-
数据库优化方案
- 优点:利用 Elasticsearch 的倒排索引,关键词查询性能提升 100 倍
-
缺点:数据同步延迟可能导致短期不一致
-
混合架构方案
- 最终选择:多级缓存 + 智能索引 + 异步处理的组合拳
- 决策依据:在保证 99.9% 查询 <50ms 的同时,支持每小时千万级数据更新
核心实现:三层架构设计
1. 多级缓存架构
采用 Guava Cache + Redis + Elasticsearch 三级存储:
// 伪代码展示多级缓存查询流程
public Skill findSkill(String keyword) {
// 第一层:本地缓存(命中率约 60%)Skill result = localCache.getIfPresent(keyword);
if (result != null) return result;
// 第二层:分布式缓存(命中率约 35%)result = redisTemplate.opsForValue().get(buildCacheKey(keyword));
if (result != null) {localCache.put(keyword, result); // 回填本地缓存
return result;
}
// 第三层:索引存储(剩余 5% 请求)result = elasticsearchClient.search(buildQuery(keyword));
if (result != null) {
// 异步更新缓存
cacheUpdateQueue.add(() -> {redisTemplate.opsForValue().set(buildCacheKey(keyword), result, 5, MINUTES);
});
}
return result;
}
2. 智能索引策略
针对不同查询模式建立专用索引:
-
倒排索引:处理关键词搜索
# Elasticsearch 映射示例 { "mappings": { "properties": {"skill_name": { "type": "text", "analyzer": "ik_max_word"}, "tag": {"type": "keyword"} } } } -
位图索引:处理多条件组合筛选
/* PostgreSQL 示例 */ CREATE INDEX idx_skill_tags_bitmap ON skills USING BITMAP(tags);
3. 异步处理机制
通过消息队列实现三个解耦:
- 数据变更 => 索引更新
- 缓存回填
- 日志收集
// Spring 事件驱动示例
@EventListener
public void handleSkillUpdateEvent(SkillUpdateEvent event) {
// 异步更新索引
rabbitTemplate.convertAndSend("index.queue",
new IndexMessage(event.getSkillId(), "UPDATE"));
// 异步清理缓存
redisCacheEvictor.evict(event.getSkillId());
}
性能测试:数字会说话
压测环境:8 核 16G 服务器 × 3,数据量 5 亿条
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| 平均延迟 | 1200ms | 28ms | 98% |
| 峰值 QPS | 150 | 8500 | 56 倍 |
| CPU 占用率 | 75% | 12% | 84% 下降 |
生产环境避坑指南
缓存一致性解决方案
采用「标记删除 + 延迟双删」策略:
- 先更新数据库
- 删除缓存并设置标记(如:
key:del) - 延迟 500ms 再次删除
- 查询时发现标记则触发主动构建
索引维护三原则
- 批量操作:使用 bulk API 减少 IO 次数
- 版本控制 :通过
_version字段处理并发冲突 - 冷热分离:历史数据迁移到单独索引
异常处理机制
设计分级降级策略:
- 一级降级:关闭复杂条件查询
- 二级降级:返回缓存快照数据
- 三级降级:静态兜底结果
总结与延伸思考
这套方案的核心思想可以复用到任何搜索密集型场景,比如:
- 电商商品搜索:用类似架构支持千人千面的推荐
- 日志分析系统:将 Elasticsearch 替换为 ClickHouse 处理时序数据
- 社交网络关系链:用图数据库优化好友推荐
关键在于识别业务中的「高频访问模式」和「数据变化特征」,没有放之四海而皆准的银弹。建议先通过埋点分析真实查询分布,再针对性优化,往往能用 20% 的优化解决 80% 的性能问题。
正文完
发表至: 技术架构
近一天内
