首页  

ELKstack中文指南之elasticsearch架构原理     所属分类 elasticsearch 浏览量 2712
根据原文整理
https://elkguide.elasticsearch.cn/elasticsearch/principle/


ES 来源于作者 Shay Banon 的第一个开源项目 Compass 库,
而这个 Java 库最初的目的只是为了给 Shay 当时正在学厨师的妻子做一个菜谱的搜索引擎。
2010 年,ES 正式发布。


2015 年初,Elasticsearch 公司召开了第一次全球用户大会 。诸多 IT 巨头纷纷赞助,参会,演讲。
会后,Elasticsearch 公司宣布改名 Elastic,公司官网也变成 http://elastic.co/。
这意味着 Elasticsearch 的发展方向,不再限于搜索业务,也就是说,Elastic Stack 等机器数据和 IT 服务领域成为官方更加注意的方向。
随后几个月,专注监控报警的 Watcher 发布 beta 版,社区有名的网络抓包工具 Packetbeat、
多年专注于基于机器学习的异常探测 Prelert 等 ITOA 周边产品纷纷被 Elastic 公司收购。



架构原理 设计工作原理 性能调优,故障处理

全文索引 倒排索引


动态更新的 Lucene 索引   近实时索引
segment in-memory buffer translog



新收到的数据写到新的索引文件里。每次生成的倒排索引, 段(segment)
commit 文件,记录索引内所有的 segment。
生成 segment 的数据来源,是内存中的 buffer

数据写入过程
内存buffer  
刷到文件系统缓存 (默认1秒刷一次)
flush到磁盘 同步更新commit 文件

commit point 

主动调用 /_refresh接口 刷到文件缓存 ,保证搜索可见

5.0 ?refresh=wait_for,写入数据后不强制刷新但一直等到刷新才返回。

curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_settings -d'
{ "refresh_interval": "10s" }
'

增加refresh时间间隔,降低实时性,提升写入性能

导入历史数据可以先完全关闭掉 refresh_interval 设置为 -1

导入完成后,手动调用 
curl -XPOST http://127.0.0.1:9200/logstash-2015.05.01/_refresh

translog
flush ,把 segment 刷到磁盘,且 commit 文件进行更新的时候, translog 文件才清空。

/_flush 接口
默认每隔30分钟 flush一次
或者当 translog 文件大小大于 512MB (老版本是 200MB)时,主动进行一次 flush。

index.translog.flush_threshold_period 
index.translog.flush_threshold_size
index.translog.flush_threshold_ops 每收到多少条数据后 flush 一次

索引数据的一致性通过 translog 保证。

默认情况下, 每隔 5 秒,或每次请求操作结束前,会强制刷新 translog 日志到磁盘上。

为了保证不丢数据,每次 index、bulk、delete、update 操作,触发刷新 translog 到磁盘上,才返回 200 OK。

提升性能可配置 
index.translog.durability=async

分布式索引 分片 shard

为了做到近实时(realtime)搜索
默认每 1 秒,都会有一个新文件产生,每个文件都需要有文件句柄,内存,CPU 使用等各种资源。
后台 segment merge
将小的 segment 合并成大的segment 文件。
后台线程执行合并任务  消耗磁盘 IO 和 CPU 

在 5.0 之前,合并线程的限速配置 indices.store.throttle.max_bytes_per_sec 是 20MB。
SSD 盘 ,写入性能高,可调整为 100M

curl -XPUT http://127.0.0.1:9200/_cluster/settings -d'
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
    }
}'

throttle 节流


5.0 开始,ES 对此作了大幅度改进
使用了 Lucene 的 CMS(ConcurrentMergeScheduler) 的 auto throttle 机制,正常情况下已经不再需要手动配置 indices.store.throttle.max_bytes_per_sec 了

合并线程数 默认数目的计算公式是: Math.min(3, Runtime.getRuntime().availableProcessors() / 2)。
一般来说就是 3 个归并线程
index.merge.scheduler.max_thread_count 

合并策略
index.merge.policy.floor_segment 默认 2MB,小于这个大小的 segment,优先被归并。
index.merge.policy.max_merge_at_once 默认一次最多归并 10 个 segment
index.merge.policy.max_merge_at_once_explicit 默认 forcemerge 时一次最多归并 30 个 segment。
index.merge.policy.max_merged_segment 默认 5 GB,大于这个大小的 segment,不用参与归并。forcemerge 除外。

减少 segment 合并消耗以及提高响应的办法
加大 flush 间隔,尽量让每次新生成的 segment 本身就比较大。

默认的最大 segment 大小是 5GB。

在负载较低的时间,通过 forcemerge 接口,强制合并 segment。

curl -XPOST http://127.0.0.1:9200/logstash-2015-06.10/_forcemerge?max_num_segments=1

由于 forcemerge 线程对资源的消耗比普通的合并线程大得多,因此绝对不建议对写入数据的热索引执行这个操作。


routing和replica的读写过程

shard = hash(routing) % number_of_primary_shards
每个数据都有一个 routing 参数,默认使用 _id 值。

索引的主分片数不可修改!!!

副本一致性

数据写入过程

客户端请求发送给 Node 1 节点,注意图中 Node 1 是 Master 节点,实际完全可以不是。
Node 1 用数据的 _id 取余计算得到应该讲数据存储到 shard 0 上。通过 cluster state 信息发现 shard 0 的主分片已经分配到了 Node 3 上。Node 1 转发请求数据给 Node 3。
Node 3 完成请求数据的索引过程,存入主分片 0。然后并行转发数据给分配有 shard 0 的副本分片的 Node 1 和 Node 2。当收到任一节点汇报副本分片数据写入成功,Node 3 即返回给初始的接收节点 Node 1,宣布数据写入成功。Node 1 返回成功响应给客户端。


wait_for_active_shards
2 个副本分片只要有 1 个成功,就可以返回给客户端了

one 写完主分片就返回,等同于 async
all 等所有副本分片都写完才能返回。

timeout 如果集群出现异常,有些分片当前不可用,默认等待 1 分钟看分片能否恢复。可以使用 ?timeout=30s 来调整

有些较大的索引,可以在做 forcemerge 前,先把副本全部取消掉,等 optimize 完后,再重新开启副本,节约单个 segment 的重复归并消耗。

curl -XPUT http://127.0.0.1:9200/logstash-mweibo-2015.05.02/_settings -d '{
    "index": { "number_of_replicas" : 0 }
}'


shard 的 allocate 控制

由 ES 自动决定 ,以下几种情况会触发分配
新建索引
删除索引
新增副本
节点增减引发的数据均衡


cluster.routing.allocation.enable
all 默认
primaries
new_primaries
none 拒绝分片分配
集群升级时会使用该参数

cluster.routing.allocation.allow_rebalance 
该参数用来控制什么时候允许数据均衡
indices_all_active 要求所有分片都正常启动成功以后,才可以进行数据均衡操作
cluster.routing.allocation.cluster_concurrent_rebalance
控制集群内同时运行的数据均衡任务个数。默认是 2 个。如果有节点增减,且集群负载压力不高的时候,可以适当加大。

cluster.routing.allocation.node_initial_primaries_recoveries 
该参数用来控制节点重启时,允许同时恢复几个主分片。默认是 4 个。如果节点是多磁盘,且 IO 压力不大,可以适当加大。

cluster.routing.allocation.node_concurrent_recoveries 
允许同时运行的数据恢复任务。默认是 2 个。
所以,节点重启时,可以看到主分片迅速恢复完成,副本分片的恢复却很慢。
除了副本分片本身数据要通过网络复制以外,并发线程本身也减少了一半。
这种设置也是有道理的,主分片一定是本地恢复,副本分片却需要走网络,带宽是有限的。

indices.recovery.concurrent_streams 
该参数用来控制节点从网络复制恢复副本分片时的数据流个数。默认是 3 个。可以配合上一条配置一起加大。
indices.recovery.max_bytes_per_sec 
该参数用来控制节点恢复时的速率。默认是 40MB。可以加大。

其他的分片分配策略。比如以 tag 和 rack_id 作为区分等
根据磁盘使用情况

cluster.info.update.interva
ES 会定时(默认 30 秒)检查各节点的数据目录磁盘使用情况。

cluster.routing.allocation.disk.watermark.low 
超出该阈值 (默认 85%) ,新索引分片不会再分配到该节点上。

cluster.routing.allocation.disk.watermark.high 
超出该阈值 (默认 90%)  ,就会触发该节点现存分片的数据均衡,把数据挪到其他节点上去。

磁盘使用水位

curl -XPUT localhost:9200/_cluster/settings -d '{
    "transient" : {
        "cluster.routing.allocation.disk.watermark.low" : "85%",
        "cluster.routing.allocation.disk.watermark.high" : "10gb",
        "cluster.info.update.interval" : "1m"
    }
}'


热索引分片不均
数据均衡策略以各节点的分片总数(indices_all_active)作为基准。
对于 Elastic Stack 场景,一般压力集中在新索引的数据写入方面。
正常运行的时候,也没有问题。但是当集群扩容时,新加入集群的节点,分片总数远远低于其他节点。
这时候如果有新索引创建,ES 的默认策略会导致新索引的所有主分片几乎全分配在这台新节点上。
整个集群的写入压力,压在一个节点上,结果很可能是这个节点直接被压死,集群出现异常。 
预先计算好索引的分片数后,配置好单节点分片的限额。

一个 5 节点的集群,索引主分片 10 个,副本 1 份。则平均下来每个节点应该有 4 个分片,那么就配置:
# curl -s -XPUT http://127.0.0.1:9200/logstash-2015.05.08/_settings -d '{
    "index": { "routing.allocation.total_shards_per_node" : "5" }
}'
注意,这里配置的是 5 而不是 4。因为需要预防有机器故障,分片发生迁移的情况。如果写的是 4,那么分片迁移会失败。


cluster.routing.allocation.balance.shard 
节点上分配分片的权重,默认为 0.45。数值越大越倾向于在节点层面均衡分片。
cluster.routing.allocation.balance.index 
每个索引往单个节点上分配分片的权重,默认为 0.55。数值越大越倾向于在索引层面均衡分片。
cluster.routing.allocation.balance.threshold 
大于阈值则触发均衡操作。默认为1。

(indexBalance (node.numShards(index) – avgShardsPerNode(index)) + shardBalance (node.numShards() – avgShardsPerNode)) <=> weightthreshold

可以采取加大 cluster.routing.allocation.balance.index,
甚至设置 cluster.routing.allocation.balance.shard 为 0 来尽量采用索引内的节点均衡。


通过 reroute 接口,手动分片分配

reroute 接口支持五种指令:allocate_replica, allocate_stale_primary, allocate_empty_primary,move 和 cancel。
常用的一般是 allocate 和 move


因为负载过高等原因,有时候个别分片可能长期处于 UNASSIGNED 状态,
可以手动分配分片到指定节点上。默认情况下只允许手动分配副本分片(即使用 allocate_replica),
如果要分配主分片,需要单独加一个 accept_data_loss 选项

curl -XPOST 127.0.0.1:9200/_cluster/reroute -d '{
  "commands" : [ {
        "allocate_stale_primary" :
            {
              "index" : "logstash-2015.05.27", "shard" : 61, "node" : "10.19.0.77", "accept_data_loss" : true
            }
        }
  ]
}'


allocate_stale_primary 表示准备分配到的节点上可能有老版本的历史数据,
运行时需要提前确认一下是哪个节点上保留有这个分片的实际目录,且目录大小最大。
然后手动分配到这个节点上。以此减少数据丢失。

因为负载过高,磁盘利用率过高,服务器下线,更换磁盘等原因,可以会需要从节点上移走部分分片:

curl -XPOST 127.0.0.1:9200/_cluster/reroute -d '{
  "commands" : [ {
        "move" :
            {
              "index" : "logstash-2015.05.22", "shard" : 0, "from_node" : "10.19.0.81", "to_node" : "10.19.0.104"
            }
        }
  ]
}'

手工 reroute 失败,响应中会带上失败的原因
从 5.0 版本开始 ,新增 allocation explain 接口,专门用来解释指定分片的具体失败原因

 节点下线

curl -XPUT 127.0.0.1:9200/_cluster/settings -d '{ "transient" :{ "cluster.routing.allocation.exclude._ip" : "10.0.0.1" } }'
自动把这个 IP 上的所有分片,都自动转移到其他节点上。等到转移完成,这个空节点就可以毫无影响的下线了。

冷热数据的读写分离

stale 不新鲜的


1 N 台机器做热数据的存储, 上面只放当天的数据。这 N 台热数据节点上面的 elasticsearc.yml 中配置 node.attr.tag: hot
2 之前的数据放在另外的 M 台机器上。这 M 台冷数据节点中配置 node.attr.tag: stale 
3 模板中控制对新建索引添加 hot 标签
  { "order" : 0, "template" : "*", "settings" : { "index.routing.allocation.include.tag" : "hot" } }
  
4 每天计划任务更新索引的配置, 将 tag 更改为 stale, 索引会自动迁移到 M 台冷数据节点


curl -XPUT http://127.0.0.1:9200/indexname/_settings -d'
{ "index": { "routing": { "allocation": { "include": { "tag": "stale" } } } } }' 


集群自动发现

ES 是一个 P2P 类型(使用 gossip 协议)的分布式系统,
除了集群状态管理以外,其他所有的请求都可以发送到集群内任意一台节点上,
这个节点可以自己找到需要转发的节点,并且直接跟这些节点通信。


所有配置了相同 cluster.name 的节点都自动归属到一个集群中。

从 2.0 版本开始,默认的自动发现方式改为了单播(unicast)方式。

考虑到节点有时候因为高负载,慢 GC 等原因可能会有偶尔没及时响应 ping 包的可能,
一般建议稍微加大 Fault Detection 的超时时间。


基于安全考虑,默认只监听本地 lo 地址

network.host: "192.168.0.2" 
discovery.zen.minimum_master_nodes: 3
discovery.zen.ping_timeout: 100s
discovery.zen.fd.ping_timeout: 100s
discovery.zen.ping.unicast.hosts: ["10.19.0.97","10.19.0.98","10.19.0.99","10.19.0.100"]

discovery.zen.ping_timeout 
仅在加入或者选举 master 主节点的时候才起作用
discovery.zen.fd.ping_timeout 
在稳定运行的集群中,master 检测所有节点,以及节点检测 master 是否可用超时。

fd  fault detection 
运行间隔和重试次数

discovery.zen.fd.ping_interval: 10s
discovery.zen.fd.ping_retries: 10

上一篇     下一篇
elasticsearch aerospike kafka副本数设置

kafka副本机制

网络杠精定律

ELKstack中文指南之elasticsearch接口使用

时区知识点整理

YAML简单介绍