新项目必须用 elastic/go-elasticsearch/v9,禁用 olivere/elastic/v7;后者已归档、不兼容 es 8+,因协议断层导致 400/406 错误,非代码问题。

新项目直接用 elastic/go-elasticsearch/v9,别碰 olivere/elastic/v7——它已归档、不兼容 ES 8+、连不上就卡在 400 或 406,不是代码问题,是协议断层。
选错客户端版本会导致请求根本发不出去
ES 8.0+ 默认禁用 _type、强制 HTTPS、改了 /_search 路径和 auth header 格式。olivere/elastic/v7 还按 7.x 协议拼 URL 和 body,一发请求就被 ES 拒绝。错误现象通常是:400 Bad Request(body 解析失败)或 406 Not Acceptable(accept header 不匹配),而不是连接超时或认证失败。
- ES 7.10–7.17 →
olivere/elastic/v7可用,但仅限存量迁移 - ES 8.0+(包括刚装的 8.15 / 9.0 alpha)→ 必须用
elastic/go-elasticsearch/v9 - 同一项目混用两个客户端 →
context.Context传参冲突、http.RoundTripper复用异常,可能静默超时
esapi.SearchRequest 必须显式设三个字段
官方客户端不补默认值,漏一个就返回空 hits 或 panic。最常漏的是 Body 类型不对、Index 为空、Context 没超时。
-
Index:不能传""或"*";生产环境禁止通配符,ES 可能拒收或打满慢日志 -
Body:必须是*bytes.Reader,json.Marshal()后得套bytes.NewReader();传map[string]interface{}会编译失败,传 raw string 容易引号嵌套出错 -
Context:必须带超时,如context.WithTimeout(ctx, 3*time.Second);不设的话 HTTP client 可能卡死在连接池,goroutine 悬停
正确示例:
req := esapi.SearchRequest{
Index: []string{"blogs"},
Body: bytes.NewReader([]byte(`{"query":{"match":{"title":"golang"}}}`)),
Context: context.WithTimeout(ctx, 3*time.Second),
}
res, err := req.Do(ctx, es)
查不到结果?先盯住分词器、refresh 和字段类型
90% 的“搜不到”不是 query 写错,而是索引侧配置和写入行为不一致。
- 中文分词:索引用了
ik_max_word,但查询没加"analyzer": "ik_max_word",ES 默认用 standard 分词,“人工智能”被切成“人工”“智能”,自然匹配不上 - refresh 延迟:文档写入后默认 1 秒才可搜索;刚
Index完立刻Search,必然为空;开发期可临时加Refresh: "true",但批量导入时严禁使用 - 字段类型错配:mapping 里
title是keyword,你却用match查——keyword不分词,match查不了;该用term查询
同步写入 MySQL + ES 时数据不可见的根源
这不是事务问题,是 Elasticsearch 的 near real-time 特性决定的:写入 → refresh → 可搜索,中间有默认 1s 窗口。MySQL 的 INSERT 是立即可见的,而 ES 不是。
- 用户点“发布”→ 立刻跳转搜索页 → 返回空列表,就是这个 gap 导致的
- 解决办法不是强行
Refresh: "true",而是前端加 loading 或后端异步轮询;生产环境应接受这个延迟,靠refresh_interval控制吞吐 - 调试时可用
Explain: true开关打开查询解释,看 ES 实际用了哪个 analyzer、是否命中倒排索引
真正麻烦的是 mapping 不一致和 analyzer 错配,这两个问题不会报错,只会安静地返回零结果——最容易被忽略,也最难排查。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











