ES 集群规划和集群调整策略
1、集群规划
1.1 集群规模
搭建一个ES集群我们需要考虑以下两方面:
- 当前数据量有多大? 数据增长情况如何?
- 机器配置如何? CPU、内存、硬盘等
集群大小设置的依据:
ES JVM heap 最大可以设置32G
30G heap大概能处理10T 的数据量, 如果内存很大 如128G, 可以在一台机器上运行多个ES节点。注意: 集群规划满足当前数据规模+ 适量增加规模即可, 后续可以按需扩展
两类应用场景:
- 用于构建业务搜索功能模块, 且多是垂直领域的搜索。数据量级几千万到数十亿级别, 一般2-4台机器规模
- 用于大规模数据的实时OLAP(联机处理分析),景点的如ELK Stack, 数据规模可能达到千亿或更多。几十到上百节点的规模
1.2 集群节点的角色分配
节点角色:
- master : node.master:true 节点作为主节点
- DataNode: node.data:true 默认是数据节点
- Coordinate node: 协调节点, 一个节点只接受、转发请求到其他节点, 汇总各节点返回数据等功能, 上面两个配置都为false的节点即为协调节点
一个节点可以充当多个角色, 默认情况下三个角色都有。
节点角色的分配:
- 小规模集群, 不需严格区分
- 重大规模集群(10个节点以上),应考虑单独的角色充当, 特别是并发查询量大,查询的合并量大,可以增加独立的协调节点。角色分开的好处是分工明确,互不影响, 不会因为协调角色而导致负载过高影响数据节点的能力
1.3 避免数据脏写(脑裂)
一个集群只有一个主节点A, 如果这时因为A节点需要处理的需求太多或网络拥堵导致从节点ping不通A节点, 这时集群会重新选择一个新的主节点B。过了一会A节点恢复了, 这时就出现了两个主节点, 这时就会出现一部分数据来源于A, 一部分数据来源于B, 从而出现数据不一致的问题。这种情况也叫脑裂。
6.x和之前的版本, 要尽量避免脑裂需要添加最小数量的主节点配置: discovery.zen.minimum_master_nodes , 参数值一般设置为 : (有master资格节点数/2)+ 1 ,这个参数控制的事选举主节点时需要看到最少有多少具有master资格的活节点才能进行选举。官方推荐 N/2 + 1, 其中N是具有master资格的节点数量
在7.x版本的ES中, 对ES集群发现系统做了调整, 不再有discovery.zen 开头的参数设置, 集群自主控制主节点状况。 且在新版本中启动了一个新的集群时需要有cluster.initial_master_nodes 初始化集群列表。
ES7中discovery.zen 配置失效, 新配置为:
中大规模集群中常用的配置:
- Master 和 dataNode 角色分离, 配置奇数个master
- 单播发现机制, 配置master资格节点(5.0之前): discovery.zen.ping.multicast.enable:false , 关闭多播发现机制, 默认即为挂壁
- 延长ping master的等待时长: discovery.zen.ping_timeout: 30 , 默认为3s, 其他节点ping主节点多长时间没有响应就认为主节点不可用, 在7.x版本中,配置换位: discovery.request_peers_timeout
1.4 索引分片数和副本数设置
索引的分片数在创建索引的时候指定, 一旦创建不可修改。
分片设置的参考原则:
- ES推荐的JVM对空间为30~32G, 所以把你的分片最大容量限制为30G, 然后再对分片数量做合理估算, 如你的数据量能达到200G, 可以最多分配7~8个分片。
- 在开始阶段一个好的方案是根据你的节点数按照1.5~3倍的原则来创建分片, 如你有3个节点,则推荐你创建的分片数不超过9个, 当性能下降时,增加节点,ES会平衡分片放置。
- 对于基于日期的索引需求, 且对索引数据的搜索场景非常少, 也许这些索引量很大, 且每个索引的数据量不大于1G , 对于这类场景, 建议只需要为索引分配1个分片, 如日志管理就是一个基于日期的索引需求, 日期索引会很多,但每个索引存方的日志数据量很少。
分片设置副本:
为保证高可用, 副本数量设置为2即可, 要求集群至少要有3个节点来分开存放主分片、副本。 如发现并发量大时, 查询性能会下降,可增加副本数,来提升并发查询能力。
注意: 新增副本时主节点会自动协调, 然后拷贝数据到新增的副本中,副本数可以随时调整。
PUT /my_index_name/_settings
{
"number_of_replicas": 2
}
2、分布式集群调优策略
这里我们主要从index写调优和Search读两个方面进行调优
2.1 Index 写调优
1、副本数置0
如果是集群首次灌入数据, 可以将副本数职位0,写入完毕后再调整回去, 这样副本分片只需要拷贝数据, 节省了索引过程。
2、自动生成doc ID
通过ES写入流程可以看出,如果写入doc是指定了id, 则ES会先尝试读取原doc的版本号以判断是否需要更新。这会涉及一次读取磁盘的操作, 但自动生成doc ID可避免该操作。
3、合理设置mappings
- 将不需要简历索引的字段index属性设置为no或not_analyzed。 最对字段不分词或不索引, 可以减少很多运算操作。尤其是binary类型, 默认情况下占用CPU很高, 且这类分词一般意义不大
- 减少字段内容长度, 如果原始数据的大段内容无需全部建立索引, 可以尽量减少不必要的内容
- 使用不同的分析器(analyzer), 不听的分析器在索引过程中原酸复杂度也有较大的差异
4、调整_source字段
source字段用来存储doc原始数据, 对不部分不需要存储的字段 ,可以通过includes 、excludes过滤, 或将source禁用, 一般用于索引和数据分离, 这样可以降低I/O 压力, 不过实际场景中大多不会禁用_source
5、对analyzed字段禁用norms
norms用于在搜索时计算doc的平分, 如果不需要平分, 可以将其禁用
"title": {
"type": "string",
"norms": {
"enabled": false
}
6、调整索引的刷新间隔
该参数却省是1s, 强制ES每秒谁信一个segment, 从而保证新写入的数据近实时的可见, 可被搜索到。 如果将该参数刷新的时间调高, 降低刷新次数,减少资源消耗, 但也牺牲了ES的实时性。
PUT /my_index/_settings
{
"index" : {
"refresh_interval": "30s"
}
}
7、批处理
批处理把多个index操作请求合并到一个batch中处理。但每次批处理多少个doc文件效率更高,受很多因素影响, 如doc的大小,字段类型、分词器等, ES官网建议一个node、一个shard做性能基准测试来确定最优值。
8、document的路由处理
当对一批中的docs进行index操作时, 该批index操作所需的线程的个数由要写入的shard的个数决定。如下图:
图中, 有2批docs写入ES, 每次需要写入4个shard, 所以共需要8个线程, 如果能减少shard数, 那么消耗的线程数也会减少。 如果两批中每批的shard个数都只有2个, 总共线程消耗个数4个, 减少一半, 如下图:
默认的routing就是id, 也可以在发送请求时,手动指定一个routing value, 如:
put /index/doc/id?routing=user_id
注意: 线程数降低,但单批的处理耗时可能增加, 和提高刷新时间间隔类似,有可能会牺牲数据的实时性。
2.2 search读优化
存储在ES中的数据超过10亿条时, 需要对其进行优化
1、数据分组
ES经常用来存储日志, 日志的索引管理方式一般基于日期的,如基于年、月、日、周等建立索引:
当搜索单天的数据, 只需要查询一个索引的shard就可以, 当需要查询多天的数据时, 需要查询多个索引的shards。 这种方案类似于数据库的分库分表、分区查询方案, 小范围数据查询。
开始的方案是建一个index, 当数据量增大时, 就扩容增加index的shard数, 当shard增大时, 要搜索的shards数也会随之上升。 基于数据分组的思路, 可以基于client进行数据分组, 每个client只需要依赖自己的index下的shard进行搜索, 而不是所有的shards, 从而提高性能。
2、使用filter 替代query
在搜索时使用query, 需要为doc的相关度打分, 使用filter, 没有打分环节处理,做的事情更少, 而且filter理论上更快一些。 如果使用上不需要打分, 直接使用filter , 如果需要打分,建议使用bool查询, bool方式可以吧打分查询和不打分查询组合到一起, 如:
GET /_search
{
"query": {
"bool": {
"must": {
"term": {
"user": "kimchy"
}
},
"filter": {
"term": {
"tag": "tech"
}
}
}
}
}
3、 ID字段定义为keywork
一般情况下, 如果字段不会被用作Range类型搜索字段, 都可以定义成keyword类型。 因为keyword会被优化,以便进行terms查询。 Integer等数字类的mapping类型, 会被优化来进行range类型搜索。
将Integer改成keyword类型后, 搜索性能可以提高30%
3、deep Paging 性能优化
3.1 深度分页性能问题
ES默认分页方式是from + size的形式, 类似于mysql的limit。 当请求数据量较大时, ES会对分页做出限制,性能消耗太大。 如一个索引分10个shards, 然后一个分页请求 from=1000, size=10, 这时会带来很严重的性能问题:cpu、 内存、IO和网络贷款等。 在query阶段, 每个shard需要返回1000条数据给coordinating node, 而coordinate node需要接收10*1000条数据, 即使每条数据只有_doc_id 和 _score , 这个数量也是很大的, 且只是针对一个查询的情况。
ES中有个配置: index.max_result_window, 默认是10000条数据, 如果分页的数据超过10000, 就拒绝返回结果。
3.2 深度分页的解决方案
1、利用scroll遍历方式
scroll分为初始化和遍历两步, 初始化时将所有符合搜索条件的搜索结果缓存起来,可以理解为快照。 在遍历是, 从这个快照中取数据, 也就是说在初始化后,对索引插入、删除、更新数据都不会影响遍历结果。 因此, scroll不适用于实时性要求较高的搜索, 更使用与后台批处理任务, 如群发等
(1)、初始化
post /student/_search?scroll=1m&size=2
{
"query":{"match_all":{}}
}
后面跟的两个参数: scroll 代表缓存暂存时间, 其他的和普通search请求相同
执行完命令后会返回一个_scroll_id, 用来下次去数据的时候使用
(2)遍历查询
get /_search/scroll
{
"scroll":"1m",
"scroll_id":"上面初始化时返回的_scroll_id"
}
这里的scroll_id是scroll初始化的唯一标识, 它可能是上一次遍历取回的_scroll_id或者是初始化返回的_scroll_id, 两个值应该是一样的。 _这里的scroll参数,是为了刷新搜索结果的缓存时间。
2、search after 方式
search after 分页是根据上一页的最后一条数据来确定下一页的位置, 同时在分页请求过程中, 如果索引的数据有增删改操作, 这些变更也会实时的反映到游标上。为了找到每一页最后一条数据, 每个文档的排序字段必须有一个全局唯一值, 一般使用_id.
get /student/_search
{
"query":{
"match_all":{}
}
"size":2,
"sort":[
{
"_id":"desc" # 指定排序字段和排序方式
}
]
}
# 使用search_after 查询下一页
get /student/_search
{
"query":{
"match_all":{}
}
"size":2,
"search_after":[3], # 从_id=3 的地方开始查询下一页数据
"sort":[
{
"_id":"desc" # 指定排序字段和排序方式, 与上面保持一直
}
]
}
3、三种分页方式比较
分页方式 | 性能 | 优点 | 缺点 | 场景 |
from + size | 低 | 灵活性好,实现简单 | 深度分页性能差 | 数据量小, 且能容忍深度分页带来的性能问题 |
scroll | 中 | 解决了深度分页问题 | 数据的实时性差,维护成本高,需要维护一个scroll_id | 海量数据的导出、 需要查询海量结果集的数据 |
search_after | 高 | 性能高, 不存在深度分页问题, 能够反应数据实时性 | 实现连续分页的实现会比较复杂, 每次查询都需要上次查询的结果 | 海量数据分页 |
推荐阅读
-
ES 集群规划和集群调整策略
-
ELasticSearch (a) ES 集群原理和构建
-
ElasticSearch 学习笔记 (4)--ES 集群的基本概念和构建流程及主要工作原理
-
es 集群的设置、原则和基本用法
-
规划、部署和维护 ElasticSearch 集群
-
PB 级大规模 Elasticsearch 集群的运行、维护和调整实践
-
使用 Elasticsearch 系列--Docker 构建 Elasticsearch 集群 Elasticsearch 系列 - ES 介绍和环境设置 Elasticsearch 使用系列-ES 增删改查基本操作 + ik 拆分器 Elasticsearch 使用系列-基本查询和聚合查询 + sql 插件 Elasticsearch使用系列-.NET6对接Elasticsearch Elasticsearch 系列-Docker 构建 Elasticsearch 集群
-
Elasticsearch] 从零开始构建 ES8 集群并集成到 Springboot 中,更好地服务于电子商务和其他需要全文索引的项目 (1)
-
ElasticSearch 系列-06]Es 集群架构和集群的核心概念
-
ElasticSearch 学习笔记 (4)--ES 集群的基本概念和构建流程及主要工作原理