赞
踩
Elasticsearch是一个基于Lucene库的开源搜索引擎,简称ES。腾讯联合 Elastic 公司在腾讯云上提供了内核增强版 ES 云服务,目前在腾讯内外部广泛应用于日志实时分析、结构化数据分析、全文检索等场景。海量规模、丰富的应用场景不断推动着腾讯云ES团队对原生ES进行持续的高可用、高性能、低成本等全方位的优化。 本文旨在介绍腾讯云ES 在优化查询性能之路上的探索历程,是对大量内外部客户不断优化实践的一个阶段性总结。本文会先从ES基本原理入手,在此基础上,从内核角度引导大家如何才能充分“压榨” ES 的查询性能。
我们首先来看下 ES 总体的查询模型。 ES 的任意节点可作为请求的协调节点,接收用户请求。协调节点将请求转发至对应一个或多个数据分片的主或者从分片进行查询,各个分片查询结果最后在协调节点汇聚,返回最终结果给客户端。
ES 的分布式查询主要有2个阶段,Query阶段跟Fetch阶段。
Query 阶段:协调节点将查询拆分成多个分片任务,发送到数据分片上通过调用Lucene 执行查 “倒排索引”,查询满足条件的文档id集合。Query 内又可以细分为2个阶段,本质上是一个基于CBO的倒排合并过程:
Fetch 阶段:归并生成最终的检索、聚合结果。Fetch 也可以细分为以下2个阶段:
ES的底层是Lucene,可以说Lucene的查询性能就决定了ES的查询性能。Lucene内最核心的倒排索引,本质上就是Term到所有包含该Term的文档的DocId列表的映射。ES 默认会对写入的数据都建立索引,并且常驻内存,主要采用了以下几种数据结构:
3. BKD-Tree:BKD-Tree是一种保存多维空间点的数据结构,主要用于数值类型(包括空间点)的快速查找。
除了索引外,ES 同时提供了行存(stored fields , _source)、列存(doc_value)来进行业务字段的存储,并提供了开启跟关闭的接口。行存跟列存各自约占一半的存储,是用户存储的大头。
1. Stored Fields :类似于MySQL 的行存,按行存储,主要用于字段值的展示,例如Kibana 。 (1) ES内置元数据字段(_index,_id,_score等等)默认开启store。 (2) 所有业务字段默认关闭store,但业务字段的store 都会被存到 _source。 (3)默认通过 index.codec 压缩算法进行压缩。查询时需要解压。 (4)内部结构:
2. _source Field : 是Stored Fields 中的一个特殊的超大字段,包含该条文档输入时的所有业务字段的原始值。 (1)大部分特性同 Stored Fields。 (2)_source 字段是该行中的第一个存储字段。优先读取。
3. doc_value Fields:类似于大数据场景中的列存,按列存储,主要用于聚合跟排序等分析场景。 (1) 不同文档的相同字段的值一起连续存储在内存中,默认不通过压缩算法压缩。可以“几乎”直接访问某个文档的某个字段。调用方式: "docvalue_fields": ["tag1"]。 (2) 数据被编码后,精度跟格式可能会发生变化。 (3)非text 默认开启doc_value。text 字段无法直接开启 doc_value。 (4) 内部结构:如下图,列式存储很容易通过字典编码跟偏移量压缩。
ES (版本>=6.6) 提供了索引生命周期管理功能。索引生命周期管理可以通过 API 或者 kibana 界面配置,详情可参考 index-lifecycle-management。使用索引生命周期管理,可以实现索引数据的自动滚动跟过期,并结合冷热分离架构进行数据的降冷跟删除。 为了让分片查询性能发挥到最优,需要对规模进行限制,我们通常有以下使用原则:
增加副本数,也可以分摊查询的负载,提升查询的性能。 考虑到用户自我管理分片容易考虑不周全,腾讯云ES推出的自研自治索引,作为一站式索引全托管解决方案,提供分片自动调优、滚动周期动态调整、查询裁剪、故障自动修复、索引生命周期管理等功能。可在降低运维与管理成本的同时,提高使用效率与读写性能。以后大家可以不用为索引生命周期管理、分片动态调整等操作烦恼了。
Elasticsearch 是面向文档型数据库,一条数据在这里就是一个文档。我们将 Elasticsearch 里存储文档数据和关系型数据库 MySQL 存储数据的概念进行一个类比。
Types 的概念已经被逐渐弱化,Elasticsearch 6.X 中,一个 index 下已经只能包含一个type,Elasticsearch 7.X 中, Type 的概念已经被删除了
Mapping的设计对于如何发挥ES的查询性能非常重要。ES 的Mapping 类似于传统关系型数据库的表结构定义。在ES 中,一旦一个字段被定义在了 mapping中,是无法被修改的(新增字段除外),所以一般我们需要修改索引的话,都会滚动或者重建索引,并采用 reindex 或logstach 来迁移数据。 为了高效发挥mapping 的性能并降低存储成本,介绍一些常见的使用技巧:
正常情况下,单个查询会扫描所有分片,容易遇到长尾效应,且大量节点在空转,可利用ES路由能力,大幅提高查询吞吐、降低长尾。通过写入时支持指定routing ,ES 会计算 target_shard_id = hash(routing) 将写入数据路由到指定分片上,这样在查询时,也可以通过指定routing,快速定位到目前数据所在的分片,查询的效率能够提升一个数量级。
具体使用方式参考:Customizing Your Document Routing | Elastic Blog。但使用这种方式需要特别注意的是,指定的 routing 须尽可能随机,保证分片之间尽量均衡,然后容易造成“热Key” 导致负载不均衡。
正如上面所说,单个查询会扫描所有分片,容易遇到长尾效应,且大量节点在空转。查询裁剪可以让查询的效率提升一个数量级。而 routing 路由优化即是分片裁剪的特例。用户也可以有其他优化用法,总结如下:
业务层面也可以直接做到(1)(2)
ES (版本>=6.0) 提供了数据排序(Index Sorting)的功能,具体用法参考Index Sorting。通过数据排序,通过牺牲少量的写入性能,在写入时将文档归类放置存储,非常有利于查询裁剪,极限压测通常大约能提升 20%-40%的查询性能,同时数据的压缩比也会有一定的提升。
我们在上面提到,ES 存储字段的类型这么多,那么我们最关心的不同类型字段的拉取性能究竟有什么区别呢? 我们基于8C 32G 规格,构建100w 条测试数据(每条数据包含100个字段)不断变化查询字段数进行查询,得到查询耗时的结果如下。我们可以看到,通过不同方式拉取字段的性能是存在一个平衡点的,大约在40左右。
(1) 当字段数很少时,低于 40 时,使用 doc_value Fields 拉取,性能最优。
分析:如果我们只需要返回其中包含的一小部分字段时,读取并解压这个巨大的_source字段可能会开销很高。
(2) 当字段超较多时,达到 40 以上时,使用 _source 变为最优。
分析:当我们需要非常多或者几乎全部字段时,此时使用 doc_value Fields 可能会有非常多的随机IO。这个时候,读取 _source 一个字段就能够处理全部业务字段。
在不同业务规模场景下,数据大小不一样,_source、列存、Store 查询性能的平衡点可能会偏移,需要实际的压测。业务可以根据需求选择最合适的存储字段。
ES 的写入模型采用的是类似LSM-Tree 的存储结构。ES 实时写入的数据都在 lucene 内存 buffer 中,同时依赖写入 translog 保证数据的可靠性。当积攒到一定程度后,将他们批量写入一个新的Segment。 这样,数据写入都是 Batch 和 Append,能达到很高的吞吐量。但是这种方式,也会产生大量的小Segment,查询时会产生非常多的随机IO,导致查询效率低下。 ES后台会进行segment merge(段合并)操作,但是默认段合并非常缓慢。所以我们可以通过强制的 forcemerge 来大幅降低Segment 数量,减少函数空转跟随机IO,极限压测通常大约能提升20%~30%的查询性能。 需要注意的是,当ES 频繁使用update 进行更新,累积到较大的数据规模时,deleted 累积过多,也会造成ES 的性能衰退(#75675),所以定期进行forcemerge 并降低 deleted ,有助于维持较好的查询性能。
缓存是加快数据检索速度的王道。ES 是使用各种缓存的大户。从整体来说,ES 可以利用的缓存汇总介绍如下:
ES中的聚合查询,类似SQL的SUM/AVG/COUNT/GROUP BY分组查询,大数据场景下的Cube/物化视图,主要用于统计分析场景。ES 聚合主要分为以下三大类:
需要注意的是,聚合不同于查询,通常普通查询是有限定size, 查够size就可以提前结束,但是聚合则每次都需要查询完全量的数据才能进行下一步的分桶、去重,如下图所示,首先每个分片内部做一次子聚合,然后所有子聚合的结果多路归并,再做一次聚合,才能得到最终的聚合结果。
所以聚合的速度通常要比普通查询慢很多。ES 的高基数聚合查询非常消耗内存,超过百万基数的聚合很容易导致节点内存不够用以至OOM,腾讯云ES 在这块的可用性方面也做了非常多的工作。那如何满足海量数据聚合分析场景的需求呢?我们通过大量实践,总结出以下4个聚合优化利器:
1. 拆分:用户可以通过 Composite Aggregation 这一类特殊的聚合,高效地对多级聚合中的所有桶进行分页。通过这种方式,我们可以将一个超大的聚合分析需求,拆分成流式的聚合查询小任务,通过不断迭代,通过较低的内存,也能跑完海量数据的聚合分析任务。特别的,term 聚合可以通过num_partitions 内置参数进行拆分。
2. 关闭缓存:上面提到,由于聚合查询的数据范围大,所以其构建缓存的开销在高基数场景下甚至能达到分钟级,term 聚合可以通过指定内置参数 "execution_hint" :"map" 关闭聚合缓存。如果不加"execution_hint": "map", ES 每次聚合会构建一次该索引全量的global_ordinals (本质是一个正排索引)用于缓存加速下一次聚合,有新写入就会导致该缓存失效,下次查询再出发全量构建。在Composite 聚合的场景下,腾讯云ES 也基于CBO 策略对高基数的聚合场景进行了优化,在高基数场景下跳过构建缓存。
3. 分片路由:通过分片路由对聚合分析任务进一步拆分,大大降低聚合的多路归并的开销。如下图:
4. 排序:通过数据排序来进行查询时的数据裁剪,可以进一步提升聚合性能。腾讯云ES 自研 Composite 聚合利用排序大幅提速,PR已经合并到官方,详参考composite-aggregation-early_termination
原生ES在实际业务压测中,我们发现如果使用FilterPath容易产生性能问题,为了进一步提升查询性能,内核优化支持裁剪查询结果。腾讯云ES 提供自研开关如下:
- PUT /_cluster/settings
- {
- "transient": {
- "search.simplify_search_results": true, // 普通查询
- "search.simplify_aggregation_results": true // Composite聚合
- }
- }
详参考:查询结果字段 裁剪优化
ES 批量拉取数据的场景下通常有以下几种方式:
腾讯云ES 基于 Search Scroll 优化内核,降低了查询过程中(反)序列化跟压缩解压的开销, 进一步优化批量拉取数据的性能,具体参考:Search Scroll 查询流程优化, Scroll 查询结果columnar格式优化。
5.12 读懂监控,跟查询慢日志
当我们需要针对性的对业务的查询场景进行分析,定位性能瓶颈时,我们首先需要读懂监控,跟慢日志。
2. 其次需要关注是否有长耗时的查询任务跟查询拒绝率,当这些指标出现异常时,说明大概率出现了大查询,导致查询线程池长期被占用,需要分析大查询并进行优化。
3. 通过慢日志,我们可以针对性的对大查询进行针对性的 profile 分析跟优化,配置方式参考Elasticsearch 中的慢日志。
4. ES 自身也提供了一些接口,可以查看节点执行查询的一些状态:
负载均衡对于最大限度发挥ES 集群的性能是非常重要的,局部出现热点或短板都容易导致集群整体的负载上不去。这里列举了几种常见的情况以及优化方式:
腾讯云ES 使用 腾讯基于社区 Open JDK 定制开发的 JDK 版本 Tencent Kona JDK, 验证跟Open JDK 相比,有更强的吞吐,更低的CPU 和内存使用率。同时使用G1GC 来减少长时GC,并通过大规模的 JVM 参数调优验证,进一步优化 GC 提升性能。这里不再展开,详参考:Elasticsearch Service Kona JDK
ES 的社区非常活跃,全球有1700+ Contributers,ES 官方也在不断的迭代更新优化,每个月都更新小版本,每年会出大版本。腾讯云ES 团队也在不断的打磨 ES 。建议用户尽量使用最新的稳定版本。
ES 版本 | 介绍 |
---|---|
7.14 | 腾讯云ES自研Search Scroll 批量拉取数据接口优化,性能提升20%,参考5.11 |
7.13 | Date Histogram 聚合内部重写为 filters 聚合,性能提升10 倍。 https://www.elastic.co/cn/blog/new-in-elasticsearch-7-13-even-faster-aggregations |
7.10 | 腾讯云ES自研X-Pack 鉴权性能优化,通过特殊权限处理、缓存、延迟加载等机制消除 CPU 热点,在当索引数量较多的场景下,提升查询性能30%+。 |
7.9 | 优化了多层嵌套聚合所消耗的内存。 |
7.0 | 通过计算跳过不必要的记录,查询大量文档的top N 性能提升3-7倍。Faster Retrieval of Top Hits in Elasticsearch with Block-Max WAND |
本文首先介绍 ES 的分布式查询模型、索引数据结构、字段存储等基本原理,然后在此基础上详尽地介绍了如何让查询性能发挥到最优的各种使用技巧,以及腾讯云ES 在性能方面所做的耕耘。希望能够帮助大家分析定位查询性能的问题,找到使用ES的最佳姿势,也希望能为未来继续挖掘ES性能抛砖引玉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。