前言
最近在做一个工具,从 日志系统 ES 集群导出数据,压缩然后上传到腾讯云存储,作为离线冷备份数据。在此过程中,遇到了数个性能问题。
其中 Elasticsearch SDK 的反序列化性能成为了一个瓶颈,因此在这里记录下处理方案。
反序列化性能
olivere/elastic
是一个常用的功能非常强大的 Go Elasticsearch 客户端 SDK,它将大部分 Elasticsearch 的请求参数封装成结构体和方法,易用程度非常高。在处理请求返回值时 olivere/elastic
会使用 Go 内建的 json
库,将请求结果反序列化为对应的结构体,非常便于后续操作。
但是当导出大量数据的时候,比如用 Search/Scroll 每批次上万条文档,进行索引导出时,经 pprof
分析,反序列化会占用大量的 CPU 循环。
因此,为了特定需求,追求极限性能,有必要对此进行特殊处理。
使用 buger/jsonparser
好在 olivere/elastic
提供了 PerformRequest
方法,返回裸字节,我们可以复用现有的工具构建请求,然后自行对返回原始字节进行处理。
这里选用了 buger/jsonparser
库,这个库可以在原始 JSON 字节上进行特定嵌套字段的搜索,和数组遍历。
相较于反序列化为结构体,再对字段进行处理,这种方法可以极大地提升性能并节约内存,确切说,不会消耗任何额外内存。经验证,反序列化所占用的 CPU 循环已经由原先的 80% 减少到 20%。
buger/jsonparser
的使用方法很简单,可以参考官方文档,也可以参考我下面这个例子:
这个例子就是从 Elasticsearch 的 Search/Scroll 结果中,获取 hits.hits
数组的 _source
字段,并调用外部回调,将 _source
字段的原始 JSON 字节传递出去。
// find hits.hits
var hitsBuf