← 返回生产场景
🔍 搜索系统
ES 全文搜索 / 分词 / 排序 / 高亮 / 热搜
🤔为什么不用 MySQL LIKE选型
| 维度 | MySQL LIKE | Elasticsearch |
| 全文搜索 | 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); // 按搜索量从高到低
}
生产场景 · 搜索系统 · 全栈资料库