【Elasticsearch】es初识,在项目架构中的用途,与mysql和kafka的配合使用,
ES是一个开源的高扩展的分布式全文检索引擎
在项目已有mysql增删改查的情况下,新增kafka,es流程
用户新增/修改商家(写MySQL)
↓
Kafka 生产者发送商家数据消息
↓
Kafka 消费者监听消息 → 写入 Elasticsearch
↓
前端搜索商家时 → 查询 Elasticsearch(不是 MySQL)
这个 Kafka + Elasticsearch 的写入同步 + 搜索解耦架构 是目前绝大多数中大型互联网企业的标准做法
一、为什么不能只查 MySQL而引入es查询?
传统做法的问题:
比如你在前端搜索商家名称“李大壮”,如果查的是 MySQL:
SELECT * FROM merchant WHERE name LIKE '%李大壮%' OR description LIKE '%李大壮%' LIMIT 10;
问题:
- 慢:LIKE + 多字段模糊查询在数据量大时性能非常差;
- 不智能:MySQL 的模糊匹配不支持分词、不支持排序打分;
- 不灵活:无法实现“相关度排序”“拼音搜索”“短语匹配”等搜索场景。
所以,企业通常引入 Elasticsearch(简称 ES)来替代 MySQL 作为 搜索引擎。
二、为什么 MySQL 改动不直接同步到 Elasticsearch,而是先发 Kafka?
这是你最关键的问题:为什么加一层 Kafka?
原因一:解耦系统,避免写 ES 出错影响主业务
-
如果你直接在写 MySQL 后同时写 ES,那么:
- 如果 ES 挂了、网络抖动、接口异常等,会导致整个商家创建流程失败;
- Kafka 相当于消息缓冲器 + 解耦层,即便 ES 挂了,Kafka 消息还能继续收集,等恢复后继续消费。
原因二:可支持异步、并发、大吞吐处理
- Kafka 是高吞吐分布式系统,支持水平扩展(通过分区);
- 消费者可以并行处理,批量写入 ES,系统更加稳定、可扩展。
原因三:可拓展更多下游系统,不只是 Elasticsearch
未来你可能还需要这些:
- 同步商家数据到 Redis 缓存(做秒级展示);
- 触发推送服务(比如新商家上线通知);
- 同步用户画像系统、数据仓库。
如果有 Kafka,你只需要新增一个消费者;
如果没有 Kafka,你要改源代码、逻辑复杂,非常不利于维护。
三、流程总结:企业为什么这样做?
阶段 | 系统 | 作用 |
---|---|---|
1 | MySQL | 业务主库,负责存储真实、结构化的数据 |
2 | Kafka | 消息中间件,做异步解耦、缓冲和流式处理 |
3 | Elasticsearch | 专用于搜索,支持全文检索、相关度评分等搜索功能 |
4 | 前端 | 直接查 ES,提升搜索速度、支持搜索功能增强 |
四、图示结构(简化):
用户操作前端↓后端写 MySQL(商家表)↓┌────→ Kafka ←────┐│ │Kafka Consumer │(可以多个系统消费)↓ ↓
ElasticSearch Redis/OLAP等其他系统↓
前端搜索
es查询使用案例
query := map[string]interface{}{// DSL 查询主体"query": map[string]interface{}{"bool": map[string]interface{}{ // bool 组合查询(企业中常用)"must": []interface{}{ // must 表示“必须匹配”的条件,全部满足才返回map[string]interface{}{"multi_match": map[string]interface{}{ // 多字段模糊匹配"query": in.Keyword, // 用户输入的搜索关键词(如“口腔”)"fields": []string{"name", "business", "address"},"type": "most_fields", // type: most_fields → 各字段各自打分后相加,适合字段内容差异大;另一个选项 cross_fields 更适合字段语义接近。},},},"filter": filters, // 精准过滤器(如按省市区筛选,不能容忍模糊匹配)"should": []interface{}{ // 加分项,提升命中相关性map[string]interface{}{"match_phrase": map[string]interface{}{ "name": in.Keyword, // 要求关键词“完整连续地”出现在 name 中才能加分(如“牙科诊所”)},},map[string]interface{}{"match_phrase": map[string]interface{}{"business": in.Keyword, // business 完整短语匹配},},},// 注:should 不设置 minimum_should_match 时,不满足不会过滤掉,只是不加分},},// 分页设置"from": (in.Page - 1) * in.PerPage, // 从第几条开始取数据(起始位置)"size": in.PerPage, // 每页返回多少条// 排序设置"sort": []interface{}{map[string]interface{}{"order_score": "desc", // 业务字段排序:order_score 是你在 merchant 映射里定义的字段,值越大代表权重越高。// 通常用于控制:优质商家排前面(如人工打分、运营配置)},},
}// -----------------
// 构造查询DSL// DSL:Domain-Specific Language(领域特定语言)// 这里表示一段用于构建或解析 Elasticsearch 查询语句的逻辑query := map[string]interface{}{"query": map[string]interface{}{"bool": map[string]interface{}{"must": []interface{}{map[string]interface{}{"multi_match": map[string]interface{}{ // multi_match 该字段使得查询支持不同类型的匹配模式// type 参数控制多字段匹配的方式,主要有两种:"query": in.Keyword,"fields": []string{"name", "business", "address"},"type": "most_fields", // 1.most_fields:把多个字段匹配结果 分开 算分,最后相加// 2.cross_fields:把多个字段 和在一起 匹配(适合同一个词,可能出现在name和business这种的情况)// 分数(score):Elasticsearch内部计算的相关度分值,用于衡量文档与查询的匹配程度,// 数值越大说明匹配越好,搜索结果排序默认会根据这个分数降序排列。},},},"filter": filters,// 其中should字段,主要作用是在满足上述条件下,增加相关度,缩小搜索范围。"should": []interface{}{ // should:表示“或”关系,满足should中条件的文档会被加分,提高其相关度排序,// 但文档不一定非要满足should中的条件才能匹配整个查询(除非设置minimum_should_match)。// 下面2map的效果:如果传入的name和business是能完全匹配的,则会获得更高的相关度评分。map[string]interface{}{"match_phrase": map[string]interface{}{ // match_phrase 是短语匹配,要求查询词,是顺序的不拆分的,否则匹配失败。"name": in.Keyword,},},map[string]interface{}{"match_phrase": map[string]interface{}{"business": in.Keyword,},},},},},"from": (in.Page - 1) * in.PerPage,"size": in.PerPage,"sort": []interface{}{ // 按照评分排序map[string]interface{}{"order_score": "desc"}, // 降序},}
https://github.com/0voice