ES 的慢查询可能会导致性能瓶颈,影响系统的响应时间和用户体验。要优化 ES 查询性能,可以从查询语句和表结构两个方面入。
从查询语句优化角度,可以优化查询类型、合理使用fliter
、限制字段返回等都是有效的方法。
从表结构优化角度,选择合适的字段类型、合理设置索引、优化分片和副本设置等也是提高性能的关键。
1. 查询语句优化
1.1 使用合适的查询类型
-
精确匹配(Term Query):
-
对于精确值匹配,使用
term
查询而不是match
查询。term
查询适用于不分词的字段,例如 ID、状态码等。 -
示例:
{ "query": { "term": { "status": "active" } } }
-
-
避免使用通配符(Wildcard Query):
-
wildcard
查询会导致全表扫描,性能较差。如果必须使用,尽量在字段上使用keyword
类型或使用前缀查询(prefix
)。 -
示例:
{ "query": { "wildcard": { "field": "value*" } } }
-
-
避免使用正则表达式(Regexp Query):
- 正则表达式查询通常非常慢。尽量避免或优化正则表达式。
-
合理使用布尔查询(Bool Query):
-
使用
bool
查询结合多个子查询时,确保使用must
,should
,must_not
等子句进行合理的组合。 -
示例:
{ "query": { "bool": { "must": [ { "match": { "field1": "value1" } }, { "range": { "field2": { "gte": 10 } } } ], "should": [ { "term": { "field3": "value2" } } ], "must_not": [ { "term": { "field4": "value3" } } ] } } }
-
-
分页优化:
-
避免使用深分页(
from
和size
的组合)。使用scroll
API 或search_after
实现高效分页。 -
示例(使用
search_after
):{ "query": { "match_all": {} }, "sort": [ { "timestamp": "asc" } ], "search_after": [ "2023-09-01T00:00:00" ] }
-
1.2 限制字段返回
-
只返回需要的字段:
-
使用
_source
参数限制返回的字段,避免检索不必要的字段。 -
示例:
{ "_source": ["field1", "field2"], "query": { "match_all": {} } }
-
1.3 使用过滤器
-
过滤器而非查询:
-
对于不需要计算相关性的查询,使用
filter
而不是query
,因为过滤器更高效且缓存友好。 -
示例:
{ "query": { "bool": { "filter": [ { "term": { "status": "active" } }, { "range": { "date": { "gte": "2023-01-01" } } } ] } } }
-
1.4 使用聚合
-
优化聚合查询:
-
对于复杂的聚合查询,使用
aggs
进行聚合,避免在查询中包含复杂的计算。 -
示例:
{ "aggs": { "status_count": { "terms": { "field": "status.keyword" } } } }
-
2. 表结构优化
2.1 适当设计字段类型
-
选择合适的数据类型:
-
根据数据的特点选择合适的字段类型,如
text
、keyword
、integer
、date
等。text
类型适合全文搜索,keyword
适合精确匹配。 -
示例:
{ "mappings": { "properties": { "status": { "type": "keyword" }, "description": { "type": "text" } } } }
-
2.2 使用合适的索引
-
使用
keyword
类型索引:-
对于需要进行精确匹配的字段,使用
keyword
类型,这样可以提高查询性能。 -
示例:
{ "mappings": { "properties": { "status": { "type": "keyword" } } } }
-
2.3 合理设置分片
-
分片和副本设置:
-
根据数据量和查询负载合理设置分片数量。更多的分片可以提高并发查询的性能,但也可能增加管理开销。副本数设置可以提高查询性能和容错能力。
- 一般控制每个分片占用的硬盘容量不超过32G(与Java使用的内存指针压缩技术有关)
- 为了防止节点故障时丢失太多数据,一般分片数也不超过节点数的3倍
-
示例:
{ "settings": { "index": { "number_of_shards": 3, "number_of_replicas": 1 } } }
-
2.4 字段数据类型优化
-
避免使用动态映射:
-
动态映射可能会创建不必要的字段,增加存储和查询的复杂度。使用显式映射定义字段类型。
-
示例:
{ "mappings": { "properties": { "field1": { "type": "text" } } } }
-
2.5 数据建模
-
优化数据建模:
-
对于复杂查询,可以考虑将数据建模为更适合查询的结构。例如,使用嵌套对象和子文档来优化查询。
-
示例:
{ "mappings": { "properties": { "user": { "properties": { "name": { "type": "text" }, "age": { "type": "integer" } } } } } }
-