简介
ES 虽然在设计架构上有非常好的搜索性能,但是随着数据量的不断增加,会有很多因素影响着 ES 的查询性能。本文从 集群规划、索引设计、以及 查询方法 的角度,介绍了关于 ES 查询优化的一些手段,本文主要参考文章如下:
集群优化
更好的硬件
如果条件允许的情况下,采用SSD,配置更大的内存以及更快的CPU。
角色分工
Master Node 仅用于管理集群,Tribe Node 仅用于对不同集群请求的转发,Data Node 仅用于对实际数据的存储与分析。
filesystem cache 留存较大容量
至少有一半的可用内存进入文件系统缓存,以便 ES 可以将索引的热区域保留在内存中。
冷热节点架构
# 将高性能节点标记为 hot
./bin/elasticsearch -Enode.attr.box_type
我们可以通过指定参数"routing.allocation.include.box_type": "hot",让所有符合命名规则索引的 Shard 都将被分配到 Hot Nodes 上:
PUT _template/active-logs
{
"template": "active-logs-*",
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1,
"routing.allocation.include.box_type": "hot",
"routing.allocation.total_shards_per_node": 2
},
"aliases": {
"active-logs": {}
}
}
同样符合命名规则索引的 Shard 会被分配到 Warm Nodes 上,我们指定了更少的 Shards 数量和复本数。注意,这里的复本数为 0,和 best_compression 级别的压缩,方便做迁移等操作,以及进行一些数据的压缩:
PUT _template/inactive-logs
{
"template": "inactive-logs-*",
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"routing.allocation.include.box_type": "warm",
"codec": "best_compression"
}
}
Rollover API 切割以后,active-logs-1 将变成一个冷索引,我们将它移动到 Warm Nodes 上。先将索引置为只读状态,拒绝任何写入操作,然后修改 index.routing.allocation.include.box_type 属性,ES 会自动移动所有 Shards 到目标 Data Nodes 上:
PUT active-logs-1/_settings
{
"index.blocks.write": true,
"index.routing.allocation.include.box_type": "warm"
}
Cluster Health API 可以查看迁移状态,完成后进行收缩操作,其实相当于复制出来一个新的索引,旧的索引还存在。
POST active-logs-1/_shrink/inactive-logs-1
到目前为止我们已经实现了索引的冷热分离,和索引的收缩,我们知道每个 Shard 下面由多个 Segment 组成,那 inactive-logs-1 的 Shard 数是 1,但 Segment 还是多个。这类索引不会在接受写入操作,为了节约空间和改善查询性能,通过 Forcemerge API 将 Segment 适量合并:
POST /active-logs-1/_forcemerge?only_expunge_deletes=false&max_num_segments=1&flush=true
最后删除 active-logs-1,因为我们已经为它做了一个查询复本 inactive-logs-1。
索引设计优化
按日期建立索引:根据索引产生数据量的速率,按照月、周、天来滚动建立索引。
字段拉平:将复合字段拆分为多个不同字段,查询时减少查询的字段个数。
提前建立 mapping:预先建立 mapping,而不是让 ES 自动生成数据类型,加速检索。
确定副本数量:max(max_failures, ceil(num_nodes/num_promaries)-1)
使用 keyword 代替 int/long
对只读索引 force-merge
查询方法优化
减少模糊匹配:match匹配(有索引),避免使用模糊匹配(wildcard)。
使用日期字段搜索范围:缩小查询的范围
使用过滤器上下文:filter过滤器子句用于回答“这个文档是否匹配这个子句”,Elasticsearch只需要回答“是”或“否”,不需要为过滤器子句计算相关性分数,而且过滤器结果可以缓存。
使用 Rounting: shard_num = hash(_routing) % num_primary_shards