以下文章来源于铭毅天下Elasticsearch ,作者铭毅天下
Elasticsearch技术基础、进阶、实战第一公众号! 和你一起,死磕ELK Stack(Elasticsearch/Logstash/Kibana/Beats)!
Elasticsearch 是一款功能强大的分布式搜索和分析引擎,被广泛应用于各种场景。
然而,在使用 Elasticsearch 进行查询操作时,可能会遇到一些限制。
这些限制有的是为了防止性能下降和资源耗尽而设定的,有些则是由于软件本身的工作方式所固有的。
深入了解这些查询限制,对于优化性能和确保系统的高效稳定运行至关重要。
本文将详细介绍 Elasticsearch 中的五大查询限制,并提供相应的解决方案和优化建议。
Elasticsearch 默认情况下会限制每个搜索查询返回的文档数量。
size
参数控制最大返回结果数,默认值为 10。
如果设置过高的 size
值,可能会导致内存使用量增加,并需要更多的 CPU 和磁盘资源来检索和评分文档。
需要注意的是,Elasticsearch 对查询结果的最大限制为 10,000 个文档。超过该值将导致以下错误:
"type": "illegal_argument_exception",
"reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [100000].
See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
这是由于 index.max_result_window
的默认值为 10,000,目的是防止深度分页导致的性能问题。
https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html
对于需要深度分页的场景,建议使用以下两种方法:
search_after
search_after
参数允许您在不增加 from
和 size
参数的情况下,进行深度分页。它的性能比深度分页要好得多。
示例:
GET /my-index/_search
{
"size": 1000,
"query": {
"match": {
"title": "Elasticsearch"
}
},
"sort": [
{ "timestamp": "asc" },
{ "_id": "asc" }
],
"search_after": [1609459200000, "my_document_id"]
}
scroll
)滚动查询适用于一次性检索大量数据的场景,如全量导出。它保持一个服务器端的快照,以确保在遍历结果集时的一致性。
示例:
GET /my-index/_search?scroll=1m
{
"size": 1000,
"query": {
"match": {
"title": "Elasticsearch"
}
}
}
注意:滚动查询不适用于实时用户请求,因为它在服务器端会消耗资源。
干货 | 全方位深度解读 Elasticsearch 分页查询
值得一提的是,咱们常用的工具 elasticdump 就是基于 scroll 实现的。
https://github.com/elasticsearch-dump/elasticsearch-dump
Elasticsearch 的核心引擎 Lucene 具有内置的保护机制,可防止执行过于庞大的查询导致系统卡顿。复杂的查询(如包含大量 OR
条件的布尔查询)可能会被重写为包含大量子句的查询,从而消耗大量的内存和计算资源。
在早期版本的 Elasticsearch 中,max_clause_count
的默认限制为 1024。
从 Elasticsearch 7.0 版本开始,此值已调整为 4096。如果查询的子句数量超过该限制,Elasticsearch 将抛出异常,拒绝执行该查询。
在 Elasticsearch 8.0.0 版本中,indices.query.bool.max_clause_count
这个设置已被废弃,不再对查询行为产生影响。
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-settings.html
Elasticsearch 现在会动态地设置查询中允许的最大子句数,使用基于以下因素的启发式算法:
该限制的最小值为 1024,但在大多数情况下会更高。例如:
最大子句计数限制的动态调整机制,使 Elasticsearch 能够更智能地管理资源,防止单个查询过度消耗内存。
然而,这也要求我们在设计查询时更加谨慎,避免过度依赖大量子句的复杂查询。
通过优化查询方式和合理配置资源,可以有效应对新的限制,确保系统的稳定性和高性能。
在聚合和排序操作中,Elasticsearch 可能需要将字段的数据加载到内存(堆)中。对于高基数(cardinality)的字段(如唯一值很多的字段),这可能导致内存使用量过高,甚至引发 OutOfMemoryError
。
可以通过设置 indices.fielddata.cache.size
来限制字段数据缓存的堆内存使用量。
indices.fielddata.cache.size: 40%
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-fielddata.html
上述配置将字段数据缓存限制为堆内存的 40%。
对于需要频繁聚合的字段,考虑在索引阶段预先计算好指标,存储为文档的一个字段,减少实时聚合的计算量。可以参考如下文章中的实战案例。
尽量避免对 text
类型的字段进行聚合,因为它们需要进行分析(分词)才能聚合。
可以使用 keyword
类型的字段替代。
复杂的多表连接接操作(如嵌套查询、父子关系查询)会给集群带来显著的负载。
如果查询过于复杂,可能会触发 max_clause_count
等限制,Elasticsearch 可能会拒绝执行此类查询。
Elasticsearch 是面向文档的存储,而非关系型数据库。试图在其中实现关系型数据库的星型或雪花型模式是不可行的。
虽然可以使用嵌套(nested
)或父子(parent-child
)关系来实现一定程度的联接,但这会增加索引和查询的复杂度,以及系统的开销。
在索引阶段,尽可能地扁平化数据结构,将相关的数据放在同一个文档中。
这可能会增加数据冗余,但会显著提高查询性能。
如果必须进行复杂的多表关联操作,考虑在应用层(如应用服务器或客户端)进行数据组合,而非依赖 Elasticsearch。
每个 Elasticsearch 集群都有其最大查询吞吐量,这取决于节点数量、硬件配置和查询的复杂度。
当查询请求超过集群的处理能力时,响应时间会增加,甚至可能导致请求被拒绝。
假设:
最大吞吐量(仅供参考):
最大查询吞吐量 = 数据节点数 × 每节点搜索线程数 / 查询耗时
最大查询吞吐量 = 3 × 13 / 1 = 39 个查询/秒
当请求数量超过线程处理能力时,新的查询会被放入搜索队列。
当队列达到最大长度时,Elasticsearch 将返回 HTTP 429 错误,拒绝新的请求。
为防止内存溢出或堆耗尽,Elasticsearch 实施了断路器机制。
当查询或操作超过预设的资源限制时,断路器会触发并中止操作,以保持系统稳定。
使用监控工具(如 Kibana、Elastic Stack Monitoring)定期监控查询的响应时间和资源消耗。
识别并优化慢查询,调整查询方式或增加必要的索引。
扩展集群:增加数据节点数量,分散查询负载。
升级硬件:提升节点的 CPU、内存和存储性能(结合公司实际,等Leader 拍板才可以)。
在发送请求时,设置合理的超时时间,避免长时间占用系统资源。
当收到 HTTP 429(Too Many Requests)错误时,不要立即重试请求。
应实现指数退避(Exponential Backoff)策略,延迟一段时间后再重试。通俗一点说:如果请求被拒绝,不要立即重试,而是先等一会儿再试,而且每次等待的时间要比上次更长。
深入了解 Elasticsearch 的查询限制,对于优化性能和确保系统稳定性至关重要。
通过合理配置参数、监控资源使用情况,以及设计合适的数据模型,可以有效避免触及这些限制,从而提升整体搜索体验。
提示:OpenSearch 与 Elasticsearch 原理相同,可以一并参考本文。
参考:https://bigdataboutique.com/blog/elasticsearch-and-opensearch-query-limits-145927