优先选用 elastic/go-elasticsearch,因其由 elastic 官方维护、支持 7.x/8.x、tls/重试/负载均衡更严谨;olivere/elastic 已归档,7.17+ 会因 accept header 不匹配返回 406 错误。

用 olivere/elastic 还是 elastic/go-elasticsearch?选错直接卡在连接阶段
Go 官方推荐的客户端是 elastic/go-elasticsearch,它由 Elastic 官方维护,支持 7.x 和 8.x,且对 TLS、重试、负载均衡等生产级特性封装更严谨;而 olivere/elastic 已归档(archive),不再更新,7.17+ 版本会出现 406 Not Acceptable 错误——因为默认发送的 Accept header 不再匹配新版本 API 的响应格式。
实操建议:
- 新项目一律用
elastic/go-elasticsearch,初始化时注意版本号要与服务端一致(如 ES 8.12 → 用v8.12.0) - 导入路径是
github.com/elastic/go-elasticsearch/v8,别漏掉/v8 - 若用 Docker 启动 ES,确保
ELASTICSEARCH_URL=http://localhost:9200可通,且关闭 X-Pack 安全认证(或配好username/password)
es.Search() 返回空结果?检查 TrackTotalHits 和 Source 配置
ES 8.x 默认禁用 track_total_hits,hits.Total.Value 会是 nil 或 0,但实际文档可能已命中;同时,若没显式启用 Source,hit.Source 是空字节切片。
常见错误现象:打印 len(res.Hits.Hits) 是 0,但 Kibana 查得到数据。
实操建议:
- 搜索时必须加
.TrackTotalHits(true)才能拿到准确总数 - 用
.FetchSource(true)或.FetchSourceInclude([]string{"title", "content"})显式开启源字段返回 - 解码前先检查
res.Hits.Hits长度,再逐个用json.Unmarshal(hit.Source, &doc)解析
怎么避免 context.DeadlineExceeded 导致查询失败?超时不是设越大越好
ES 查询本身有默认超时(如 search_timeout),但 Go 客户端还叠加了 HTTP client 超时。两者不协调就会出现 context.DeadlineExceeded,尤其在模糊查询或聚合多字段时。
实操建议:
- 创建 client 时传入自定义
http.Client,设置Timeout为 30s,IdleConnTimeout≥ 60s(避免连接池过早断开) - 每次
Search()必须带带 timeout 的 context:ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - 不要全局复用同一个
context.Context,每个请求生成独立 ctx,用完调cancel() - 聚合查询(
aggs)比普通 match 查询更耗时,建议单独设 10s 超时
批量写入用 BulkIndexRequest 还是 bulk.BulkIndexer?性能差 3 倍以上
bulk.BulkIndexer 是官方推荐的异步批量工具,内部自动分片、重试、背压控制;而手写 BulkIndexRequest 发送原始 JSON 数组,容易触发 413 Request Entity Too Large 或丢数据。
实操建议:
- 初始化
bulk.NewBulkIndexer时设NumWorkers: 4(根据 CPU 核数调整)、FlushInterval: 30 * time.Second - 每条文档用
bulk.Indexer.Add提交,不要自己拼index+data行协议 - 写完记得调
indexer.Close(),否则缓冲区残留数据不会刷出 - 错误通过
indexer.Stats().Failed统计,单条失败不影响其他文档
真正影响吞吐的是 bulk size 和 worker 数,不是单次请求的并发量。别在循环里反复 new indexer,那会拖垮连接池。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











