ElasticSearch学习(三):Elasticsearch 分布式与调优

1. Elasticsearch之分布式特性介绍

1. 分布式介绍及cerebro

  • es支持集群模式,是一个分布式系统,其好处主要有两个:
    • 增大系统容量,如内存,磁盘,使得es集群可以支持PB级的数据
    • 增大系统可用性,即使部分节点停止服务,整个集群依然可以正常服务
  • es集群由多个es实例组成
    • 不同集群通过集群名字来区分,可通过cluster.name进行修改,默认为elasticsearch
    • 每个es实例本质上是一个JVM进程,且拥有自己的名字,通过node.name进行修改

2. 构建集群

  • 通过如下命令可以启动一个es节点实例
bin/elasticsearch -Ecluster.name=my_cluster -Epath.data=my_cluster_node1 -E node.name=node1 -Ehttp.port=5200 -d
  • es集群相关的数据称为cluster state,主要记录如下信息:
    • 节点信息,比如节点名称,连接地址等
    • 索引信息,比如索引名称,配置等
  • 可以修改cluster state的节点称为master节点,一个集群只能有一个
  • cluster state存储在每个节点上,master维护最新版本并同步给其他节点
  • master节点是通过集群中所有节点选举产生的,可以被选举的节点称为master-eligible节点,相关配置如下:
    • node.master:true
  • 处理请求的节点即为coordinating节点,该节点为所有节点的默认角色,不能取消
    • 路由请求到正确的节点处理,比如创建索引的请求到master节点
  • 存储数据的节点即为data节点,默认节点都是data类型,相关配置如下:
    • node.data:true
  • 运行如下命令可以启动一个es节点实例
bin/elasticsearch -Ecluster.name=my_cluster -Epath.data=my_cluster_node2 -E node.name=node2 -Ehttp.port=5300 -d

3. 副本与分片

提高系统可用性

  • 服务可用性
    • 2个节点的情况下,允许其中1个节点停止服务
  • 数据可用性
    • 引入副本(Replication)解决
    • 每个节点上都有完备的数据

增大系统容量

  • 如何将数据分布于所有节点上?
    • 引入分片(Shard)解决问题
  • 分片是es支持PB级别数据的基石
    • 分片存储了部分数据,可以分布于任意节点上
    • 分片数据在索引创建时指定且后续不允许再更改,默认为5个
    • 分片有主分片和副本分片之分,以实现数据的高可用
    • 副本分片的数据由主分片同步,可以有多个,从而提高读取的吞吐量

副本与分片

4. 两个问题

  • 此时增加节点是否能提高test_index的数据容量?
    • 不能。因为只有三个分片,已经分布在3台节点上,新增的节点无法利用
  • 此时增加副本数是否能提高test_index的读取吞吐量?
    • 不能。因为新增的副本也是分布在这3个节点上,还是利用了同样的资源。如果要增加吞吐量,还需要新增节点
  • 分片数的设定很重要,需要提前规划好
    • 过小会导致后续无法通过增加节点实现水平扩容
    • 过大会导致一个节点上分布过多分片,造成资源浪费,同时会影响查询性能

5. 集群状态

  • 通过如下api可以查看集群健康状态,包括以下三种:
    • green健康状态,指所有主副分片都正常分配
    • yellow指所有主分片都正常分配,但是有副本分片为正常分配
    • red有主分片为分配
GET _cluster/health

{
  "cluster_name" : "elasticsearch",
  "status" : "yellow",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 21,
  "active_shards" : 21,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 20,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 51.21951219512195
}

6. 故障转移

  • 集群由3个节点组成,此时集群状态是green
  • node1所在机器宕机导致服务终止,此时集群会如何处理?
    • node2和node3发现node1无法响应一段时间后会发起master选举,比如这里选择node2为master节点,此时由于主分片P0下线,集群状态变为Red
    • node2发现主分片P0未分配,将R0提升为主分片,此时由于所有主分片都正常分配,集群状态变为Yellow
    • node2为P0和P1生成新的副本,集群状态变为绿色

7.文档分布式存储

  • 文档最终会存储在分片上:
    • Document1最终存储在分片P1上
  • Document1是如何存储到分片P1的?选择P1的依据是什么?
    • 需要文档到分片的映射算法
    • 目的:使得文档均匀分布在所有分片上,以充分利用资源
    • 算法:
      • 随机选择或者round-robin算法?
      • 不可取,因为需要维护文档到分片的映射关系,成本巨大
      • 根据文档值实时计算对应的分片
  • es通过如下的公式计算文档对应的分片
    • hash算法保证可以将数据均匀的分散在分片中
    • routing是一个关键参数,默认是文档id,也可以自行指定
    • number_of_primary_shards是主分片数
shard = hash(routing) % number_of_primary_shards
  • 该算法与主分片数相关,这也是分片数一旦确定后便不能更改的原因

文档创建的流程

文档创建的流程

文档读取的流程

文档读取的流程

8. 脑裂问题

  • 脑裂问题,英文为split-brain,是分布式系统中的经典网络问题
    • 3个节点组成的集群,突然node1的网络和其他两个节点中断
    • node2与node3会重新选举master,比如node2成为了新master,此时会更新cluster state
    • node1自己组成集群后,也会更新cluster state
  • 同一个集群有两个master,而且维护不同的cluster state,网络恢复后无法选择正确的master
  • 解决方案为仅在可选举master-eligible大于等于quorum时才可以进行master选举
    • quorum = master-eligible节点数/2 + 1,例如3个master-eligible节点时,quorum为2
    • 设定discovery.zen.minimum_master_nodes为quorum即可避免脑裂

9. shard详解

倒排索引的不可更改

  • 倒排索引一旦生成,不能更改
  • 其好处如下:
    • 不用考虑并发写文件的问题,杜绝了锁机制带来的性能问题
    • 由于文件不再更改,可以充分利用文件系统缓存,只需载入一次,只要内存足够,对该文件的读取都会从内存读取,性能高
    • 利于生成缓存数据
    • 利于对文件进行压缩存储,节省磁盘和内存存储空间
  • 坏处为需要写入新文档时,必须重新构建倒排索引文件,然后替换老文件后,新文档才能被检索,导致文档实时性差

倒排索引的不可更改

文档搜索实时性

  • 解决方案是新文档直接生成新的倒排索引文件,查询的时候同时查询所有的倒排文件,然后做结果的汇总计算即可
  • Lucene便是采用了这种方案,它构建的单个倒排索引称为segment,合在一起称为Index,与ES中的Index概念不同,ES中的一个Shard对应一个Lucene Index
  • Lucene会有一个专门的文件来记录所有的segment信息,称为commit point

文档搜索实时性 - refresh

  • segment写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将segment在缓存中创建并开放查询来进一步提升实时性,该过程在es中被称为refresh
  • 在refresh之前文档会先存储在一个buffer中,refresh时将buffer中的所有文档清空并生成segment
  • es默认每1秒执行一次refresh,因此文档的实时性被提高到1秒,这也是es被称为近实时(Near Real Time)的原因
  • refresh发生的时机主要有如下几种情况:
    • 间隔时间达到时,通过index.settings.refresh_interval来设定,默认是1秒
    • index.buffer占满时,其大小通过indices.memory.index_buffer_size设置,默认为jvm heap的10%,所有shard共享
    • flush发生时也会发生refresh

文档搜索实时性 - translog

  • 如果内存中的segment还没有写入磁盘前发生了宕机,那么其中的文档就无法恢复了,如何解决这个问题?
    • es引入translog机制,写入文档到buffer时,同时将该操作写入translog
    • translog文件会即时写入磁盘(fsync),6.X默认每个请求都会落盘,可以修改为每5秒写一次,这样风险便是丢失5秒的数据,相关配置为index.translog.*
    • es启动时会检查translog文件,并从中恢复数据

文档搜索实时性 - flush

  • flush负责将内存中的segment写入磁盘,主要做如下的工作:
    • 将translog写入磁盘
    • 将index buffer清空,其中的文档生成一个新的segment,相当于一个refresh操作
    • 更新commit point并写入磁盘
    • 执行fsync操作,将内存中的segment写入磁盘
    • 删除旧的translog文件
  • flush发生的时机主要有如下几种情况:
    • 间隔时间达到时,默认是30分钟,5.X之前可以通过index.translog.flush_threshold_period控制,之后无法修改
    • translog占满时,其大小可以通过index.translog.flush_threshold_size控制,默认是512mb,每个index有自己的translog

文档搜索实时性 - 删除与更新文档

  • segment一旦生成就不能更改,那么如果你要删除文档该如何操作?
    • Lucene专门维护一个.del的文件,记录所有已经删除的文档,注意.del上记录的是文档在Lucene内部的id
    • 在查询结果返回前会过滤掉.del中的所有文档
  • 更新文档如何进行呢?
    • 首先删除文档,然后再创建新文档

整体视角

整体视角

Segment Merging

  • 随着segment的增多,由于一个查询的segment数增多,查询速度会很慢
  • es会定时在后台进行segment merge的操作,减少segment的数量
  • 通过force_merge api可以手动强制做segment merge的操作

2. Elasticsearch 篇之集群调优建议

1. 生产环境部署建议

  • 遵照官方建议设置所有的系统参数
  • 参见文档"Setup Elasticsearch -> Important System Configuration"
    • Disable swapping
    • Increase file descriptors
    • Ensure sufficient virtual memory
    • Ensure sufficient threads
    • JVM DNS cache settings
    • Temporary directory not mounted with noexec

ES设置尽量简洁

  • elasticsearch.yml中尽量只写必备的参数,其他可以通过api动态设置的参数都通过api来设定
  • 参见文档"Setup Elasticsearch -> Important Elasticsearch Configuration"
    • Path settings
    • Cluster name
    • Node name
    • Network host
    • Discovery settings
    • Heap size
    • Heap dump path
    • GC logging
    • Temp directory
  • 随着ES的版本升级,很多网络流传的配置参数已经不再支持,因此不要随便复制别人的集群配置参数

elasticsearch.yml中建议设定的基本参数

  • cluster name
  • node name
  • node.master/node.data/node.ingest
  • network.host建议显示指定为内网ip,不要偷懒直接设定为0.0.0.0
  • discovery.zen.ping.unicast.host设定集群其他节点地址
  • discovery.zen.minimum_master_nodes一般设定为2
  • path.data/pata.log
  • 处上述参数外再根据需要增加其他的静态配置参数

动态设定的基本参数

  • 动态设定的参数有transient和persistent两种配置,前者在集群重启后会消失,后者不会,但两种设定都会覆盖elasticsearch.yml中的配置

关于JVM内存设定

  • 不要超过31GB
  • 预留一半内存给操作系统,用来做文件缓存
  • 具体大小根据该node要存储的数据量来估算,为了保证性能,在内存和数据量间有一个建议的比例
    • 搜索类项目的比例建议在1:16以内
    • 日志类项目的比例建议在1:48 ~ 1:96
  • 假设总数据量大小为1TB,3个node,1个副本,那么每个node要存储的数据量为2TB/3=666GB,即700GB左右,做20%的预留空间,每个node要存储大约850GB的数据
    • 如果是搜索类项目,每个node内存大小为850GB/16=53GB,大于31GB。31*16=496,即每个node最多存储496GB数据,所以至少需要5个node
    • 如果是日志类型项目,每个node内存大小为850GB/48=18GB,因此3个节点足够

2. 写性能优化

ES写数据过程

  • refresh
    • segment写入磁盘的过程依然很耗时,可以借助文件系统缓存的特性,先将segment在缓存中创建并开放查询来进一步提升实时性,该过程在es中被称为refresh
    • 在refresh之前文档会先存储在一个buffer中,refresh时将buffer中的所有文档清空并生成segment
    • es默认每1秒执行一次refresh,因此文档的实时性被提高到1秒,这也是es被称为近实时(Near Real Time)的原因
  • tanslog
    • 如果在内存中的segment还没有写入磁盘前发生了宕机,那么其中的文档就无法恢复了,如何解决这个问题?
    • es引入translog机制,写入文档到buffer时,同时将该操作写入translog
    • translog文件会即时写入磁盘(fsync),6.X默认每个请求都会落盘,可以修改为每5秒写一次,这样的风险便是丢失5秒的数据
    • es启动时会检查translog文件,并从中恢复数据
  • flush
    • 负责将内存中的segment写入磁盘,主要做如下的工作:
    • 将translog写入磁盘
    • 将index buffer清空,其中的文档生成一个新的segment,相当于一个refresh操作
    • 更新commit point并写入磁盘
    • 执行fsync操作,将内存中的segment写入磁盘
    • 删除旧的translog文件

写性能优化

  • 目标是增大写吞吐量 - EPS(Event Per Second)越高越好
  • 优化方案
    • 客户端:多线程写,批量写
    • ES:在高质量数据建模的前提下,主要是在refresh,translog和flush之间做文章

refresh

  • 目标为降低refresh的频率
    • 增大refresh_interval,降低实时性,以增大一次refresh处理的文档数,默认是1s,设置为-1直接禁止自动refresh
    • 增大index_buffer_size,参数为indices.memory.index_buffer_size(静态参数,需要设定在elasticsearch.yml中),默认为10%

tanslog

  • 目标是降低translog写磁盘的频率,从而提高写效率,但会降低容灾能力
    • index.translog.durability设置为async,index.translog.sync_interval设置需要的大小,比如120s,那么translog会改为每120s写一次磁盘
    • index.translog.flush_threshold_size默认为512mb,即translog超过该大小时会触发一次flush,那么调大该大小可以避免flush的发生

flush

  • 目标为降低flush的次数,在6.X可优化的点不多,多为es自动完成

其他

  • 副本设置为0,写入完毕再增加
  • 合理的设计shard数,并保证shard均匀的分配在所有node上,充分利用所有node的资源
    • index.routing.allocation.total_shards_per_node限定每个索引在每个node上可分配的总主副分片数
    • 5个node,某索引有10个主分片,1个副本,上述值应该设置为多少?
      • (10+10)/5 = 4
      • 实际要设置为5个,防止在某个node下线时,分片迁移失败的问题

写性能优化

  • 主要为index级别的设置优化

3. 读性能优化

  • 读性能主要受以下几方面影响:
    • 数据模型是否符合业务模型?
    • 数据规模是否过大?
    • 索引配置是否优化?
    • 查询语句是否优化?

数据建模

  • 高质量的数据建模是优化的基础
    • 将需要通过script脚本动态计算的值提前算好作为字段存到文档中
    • 尽量使得数据模型贴近业务模型

数据规模

  • 根据不同的数据规模设定不同的SLA
    • 上万条数据与上千万条数据性能肯定存在差异

索引配置调优

  • 索引配置优化主要包括如下:
    • 根据数据规模设置合理的主分片数,可以通过测试得到最适合的分片数
    • 设定合理的副本数目,不是越多越好

查询语句调优

  • 查询语句调优主要有以下几种常见手段:
    • 尽量使用Filter上下文,减少算分的场景,由于Filter有缓存机制,可以极大提升查询性能
    • 尽量不适用Script进行字段计算或者算分排序等
    • 结合profile,explain API分析慢查询语句的症结所在,然后再去优化数据模型

4. 如何设定shard数

  • ES的性能基本是线性扩展的,因此我们只要测出1个Shard的性能指标,然后根据实际性能需求就能算出需要的Shard数。比如但Shard写入eps是10000,而线上eps需求是50000,那么你需要5个shard(实际还要考虑副本的情况)
  • 测试1个shard的流程如下:
    • 搭建与生产环境相同配置的单节点集群
    • 设定一个单分片零副本的索引
    • 写入实际生产数据进行测试,获取写新能指标
    • 针对数据进行查询请求,获取读性能指标
  • 压测工具可以采用esrally

5. xpack监控功能介绍

  • 官方推出的免费集群监控功能

最后

大家可以关注我的微信公众号一起学习进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值