查询语法层面的优化方法
1. 如只文档的 doc_ic
,则可配置 "_source": false
如果我们只需要文档的 doc_id
而不需要文档 _source
中的任何字段,那么则可以添加配置 "_source": false
。此时,ES 将只需要执行查询的 query 阶段而不需要执行 fetch 阶段,从而极大地加快查询速度。
修改前:
GET /my-index-000001/_search?
{
"query": {
"match_all": {}
},
"_source": ""
}
修改后:
GET /my-index-000001/_search?
{
"query": {
"match_all": {}
},
"_source": false
}
2. 将 query 查询改为 filter 过滤语句
使用 FilterContext 代替 QueryConext,因为 filter 查询子句的性能优于 query 查询子句,filter 查询子句不需要计算相关性的分值而 query 查询子句需要计算相关性的分支,filter 查询子句的结果可以缓存。
修改前:
GET /my-index-000001/_search?
{
"query": {
"term": {
"field_name": "field_value"
}
}
}
修改后:
GET /my-index-000001/_search?
{
"query": {
"bool": {
"filter": {
"term": {
"field_name": "field_value"
}
}
}
}
}
3. 在不超时的前提下,增加每次 scroll 获取的记录数
我们可以通过增加 size
,使每一次 scroll 返回更多数据,从而减少查询的 query、fetch、response 各阶段的次数,提高效率。但需要注意增大超时时间,以避免因为每次 scroll 将返回更多数据而超时。
修改前:
GET /my-index-000001/_search?
{
"query": {
"match_all": {}
},
"size": 1000
}
修改后:
GET /my-index-000001/_search?
{
"query": {
"match_all": {}
},
"size": 10000
}
4. 在 scroll 查询中按 _doc
排序
在 ElasticSearch 的官方文档中,说明了 _doc
表示按索引顺序排序,是最高效的排序方法。如果不关心返回文档的顺序的话,那么就应按 _doc
进行排序以提高查询性能。这在 scroll
时格外有效。
文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.6/sort-search-results.html
修改前:
{
"query": {
"bool": {
"filter": {
"term": {
"field_name": "field_value"
}
}
}
}
}
修改后:
{
"query": {
"bool": {
"filter": {
"term": {
"field_name": "field_value"
}
}
}
},
"sort": ["_doc"]
}
5. 减少不需要的查询字段(使用 _source
筛选)
通过减少不需要的字段,可以有效地减少查询的 fetch 阶段的耗时。
修改前:
GET /my-index-000001/_search?
{
"query": {
"match_all": {}
}
}
修改后:
GET /my-index-000001/_search?
{
"query": {
"match_all": {}
},
"_source": ["need-field-1", "need-field-2"]
}
6. 避免使用模糊匹配
7. 使用 filter_path 过滤返回结果
通过添加 filter_path
,可以减少网络 IO 占用。需要注意,如果使用 scroll 的话,需要保留 _scroll_id
字段。
8. scroll scan(2.1 版本前)
在 2.1 版本后已不支持。
9. search after
经过测试,在 7.10.2 版本环境下,在不使用 PIT 时:使用 _doc
排序时,search after 的全量查询速度与 scroll 的全量查询速度似乎基本相同,但可能会导致漏掉少量数据;使用 _id
排序时,search after 的全量查询速度比 scroll 的全量查询速度明显更慢?(以上测试结果与部分文章结果不一致,尚待继续分析)
response = es_client.search(
index="my-index-0001",
size=10000,
body={"query": {...}, "sort": ["_doc"]}
)
while response["hits"]["hits"]:
last = None
for item in response["hits"]["hits"]:
last = item["sort"]
# ...... 处理逻辑
response = es_client.search(
index="my-index-0001",
size=10000,
body={"query": {...}, "search_after": last, "sort": ["_doc"]}
)
10. search after + PIT(并发方法)
在 7.10 版本添加到 X-Pack 中;在 7.11 版本被添加。
文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/8.7/point-in-time-api.html
11. slice scroll(并发方法)
通过 slice scroll 可以支持并发 scroll。
但是如果没有合适的字段作为 slice field 的话,如切片(slice)数量超过索引的分片数(shards),则 ES 则需要较长
索引设计方面的优化方法
1. 将字符串格式字段改为数字或日期格式
因为索引方式的原因,使得:对于字符串类型的字段,range
过滤时效率很低;而对于数字和日期类型字段,range
过滤时效率很高。所以,如果某个字段的含义就是数字或日期,那么则不应使用字符串类型存储。
2. 降低嵌套层级
可以通过将深层嵌套(例如 "field-1": {"field-1-1": 1, "field-1-2": 2}
)的字段拉平,减少嵌套层级。
3. 减少 refresh 频率
如果对搜索的时效性要求不高,则可以将 refresh 的周期延长,减少刷新次数,但同时也意味着需要更高的内存占用。
4. 减少副本数量
以降低可用性为代价,减少副本数量,提高写索引的速度。