← 返回生产场景

🔍 搜索系统

ES 全文搜索 / 分词 / 排序 / 高亮 / 热搜

🤔为什么不用 MySQL LIKE选型
维度MySQL LIKEElasticsearch
全文搜索LIKE '%关键词%' → 全表扫,慢倒排索引,毫秒级
分词❌ 不支持中文分词✅ IK分词器("乒乓球拍"→乒乓球+拍)
相关度排序❌ 只能按时间/销量排✅ TF-IDF 打分,最相关的排前面
聚合统计GROUP BY 慢✅ facet 聚合快
高亮❌ 要自己实现✅ 原生支持 highlight
📦ES + MySQL 数据同步高频
商品数据在 MySQL,搜索走 ES。怎么保证两边数据一致?
方案原理优缺点
同步双写写MySQL的同时写ES简单但耦合,一个失败就不一致
异步MQ写MySQL后发MQ,消费者写ES解耦但延迟几秒
Canal订阅binlog监听MySQL binlog自动同步ES✅ 推荐:完全解耦,对应用透明
// Spring Data ES 使用示例 @Document(indexName = "product") public class ProductDoc { @Id private Long id; @Field(type = FieldType.Text, analyzer = "ik_max_word") private String name; // 中文分词 @Field(type = FieldType.Keyword) private String category; // 不分词,精确匹配 @Field(type = FieldType.Double) private Double price; } // 搜索 + 高亮 + 分页 public SearchHits<ProductDoc> search(String keyword, int page) { NativeSearchQuery query = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(keyword, "name")) .withHighlightFields(new HighlightBuilder.Field("name")) .withPageable(PageRequest.of(page, 20)) .build(); return esTemplate.search(query, ProductDoc.class); }
🔥热搜词实现
用户搜索时记录搜索词,统计热门搜索展示给其他用户。
// 用 Redis ZSet 实现热搜榜 public void recordSearch(String keyword) { redisTemplate.opsForZSet().incrementScore("hot:search", keyword, 1); // 每次搜索 +1 分 } public List<String> getHotKeywords() { Set<String> top10 = redisTemplate.opsForZSet() .reverseRangeByScore("hot:search", 0, Double.MAX_VALUE, 0, 10); return new ArrayList<>(top10); // 按搜索量从高到低 }
生产场景 · 搜索系统 · 全栈资料库