写点什么

分布式实时搜索和分析引擎——Elasticsearch

  • 2023-04-17
    湖南
  • 本文字数:6258 字

    阅读完需:约 21 分钟

分布式实时搜索和分析引擎——Elasticsearch

一、概述

Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了具有 HTTP Web 界面和无架构 JSON 文档的分布式,多租户能力的全文搜索引擎。Elasticsearch 是用 Java 开发的,根据 Apache 许可条款作为开源发布。

二、节点类型 &作用

1)master 节点(主节点)

配置

node.master:truenode.data:false(这里也可以配置成node.data:true)
复制代码

【注意】node.master 和 node.data 默认都是 true, 但还是建议显式配置

作用

  • 索引的创建或删除

  • 跟踪哪些节点是集群的一部分

  • 决定哪些分片分配给相关的节点

2)data 节点(数据节点)

配置

node.master:false(这里也可以配置成node.master:true)node.data:true
复制代码

作用

  • 存储索引数据

  • 对文档进行增删改查,聚合操作

3)Coordinating 节点(协调节点/Client 节点)

每一个节点都隐式的是一个协调节点,Coordinating 节点是负责接收任何 Client 的请求,包括 REST Client 等。该节点将请求分发到合适的节点,最终把结果汇集到一起。一般来说,每个节点默认起到了 Coordinating 节点 的职责。配置

node.master:falsenode.data:false
复制代码

作用

  • 处理路由请求

  • 处理搜索

  • 分发索引

4)Ingest 节点(预处理节点)

在索引数据之前可以先对数据做预处理操作,所有节点其实默认都是支持 Ingest 操作的,也可以专门将某个节点配置为 Ingest 节点。配置

node.ingest:true
复制代码

作用

  • Ingest 节点和集群中的其他节点一样,但是它能够创建多个处理器管道,用以修改传入文档。类似 最常用的 Logstash 过滤器已被实现为处理器。

  • Ingest 节点 可用于执行常见的数据转换和丰富。 处理器配置为形成管道。 在写入时,Ingest Node 有 20 个内置处理器,例如 grok,date,gsub,小写/大写,删除和重命名

  • 在批量请求或索引操作之前,Ingest 节点拦截请求,并对文档进行处理。

三、Elasticsearch 是如何实现 master 选举的

前提条件

  • 只有候选主节点(master:true)的节点才能成为主节点。

  • 最小主节点数(min_master_nodes)的目的是防止脑裂。

实现步骤

  • 第一步:确认候选主节点数达标,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes;

  • 第二步:对所有候选主节点根据 nodeId 字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第 0 位)节点,暂且认为它是 master 节点。

  • 第三步:如果对某个节点的投票数达到一定的值(候选主节点数 n/2+1)并且该节点自己也选举自己,那这个节点就是 master。否则重新选举一直到满足上述条件。

【补充】master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http 功能。

四、如何解决 ES 集群的脑裂问题

【原因】

所谓集群脑裂,是指 Elasticsearch 集群中的节点(比如共 20 个),其中的 10 个选了一个 master,另外 10 个选了另一个 master 的情况。

【解决】

当集群 master 候选数量不小于 3 个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题

五、调优

1)部署时,对 Linux 的设置优化方法

  • 关闭缓存 swap;

  • 堆内存设置为:Min(节.点内存/2,理想是 32GB);

  • 设置最大文件句柄数;

  • 线程池+队列大小根据业务需要做调整;

  • 磁盘存储 raid 方式——存储有条件使用 RAID10,增加单节点性能以及避免单节点存储故障。

2)写入调优

  • 写入前副本数设置为 0;

  • 写入前关闭 refresh_interval 设置为-1,禁用刷新机制;

  • 写入过程中:采取 bulk 批量写入;

  • 写入后恢复副本数和刷新间隔;

  • 尽量使用自动生成的 id。

3)查询调优

  • 禁用 wildcard(wildcard 检索可以定义为:支持通配符的模糊检索。类似于 mysql 的 like);

  • 禁用批量 terms(成百上千的场景);

  • 充分利用倒排索引机制,能 keyword 类型尽量 keyword;

  • 据量大时候,可以先基于时间敲定索引再检索;

  • 设置合理的路由机制。

六、Elasticsearch 索引文档的过程

这里的索引文档应该理解为文档写入 ES,创建索引的过程。文档写入包含:单文档写入和批量 bulk 写入,这里只解释一下:单文档写入流程。


  • 第一步:客户写集群某节点写入数据,发送请求。(如果没有指定路由/协调节点,请求的节点扮演路由节点的角色。)

  • 第二步:节点 1 接受到请求后,使用文档_id 来确定文档属于分片 0。请求会被转到另外的节点,假定节点 3。因此分片 0 的主分片分配到节点 3 上。

  • 第三步:节点 3 在主分片上执行写操作,如果成功,则将请求并行转发到节点 1 和节点 2 的副本分片上,等待结果返回。所有的副本分片都报告成功,节点 3 将向协调节点(节点 1)报告成功,节点 1 向请求客户端报告写入成功。

七、索引模板

索引模板,简而言之,是一种复用机制,就像一些项目的开发框架如 Laravel 一样,省去了大量的重复,体力劳动。当新建一个 Elasticsearch 索引时,自动匹配模板,完成索引的基础部分搭建。

典型的如下所示:


{ "order": 0, "template": "sample_info*", "settings": { "index": { "number_of_shards": "64", "number_of_replicas": "1" } }, "mappings": { "info": { "dynamic_templates": [ { "string_fields": { "mapping": { "analyzer": "only_words_analyzer", "index": "analyzed", "type": "string", "fields": { "raw": { "ignore_above": 512, "index": "not_analyzed", "type": "string" } } }, "match_mapping_type": "string", "match": "*" } } ], "properties": { "user_province": { "analyzer": "lowercase_analyzer", "index": "analyzed", "type": "string", "fields": { "raw": { "ignore_above": 512, "index": "not_analyzed", "type": "string" } } } } } }, "aliases": {}}
复制代码

上述模板定义,看似复杂,拆分来看,主要为如下几个部分:

{  "order": 0,                               // 模板优先级  "template": "sample_info*",               // 模板匹配的名称方式  "settings": {...},                        // 索引设置  "mappings": {...},                        // 索引中各字段的映射定义  "aliases": {...}                          // 索引的别名}
复制代码

1)模板优先级

一个模板可能绝大部分符合新建索引的需求,但是局部需要微调,此时,如果复制旧的模板,修改该模板后,成为一个新的索引模板即可达到我们的需求,但是这操作略显重复。此时,可以采用模板叠加与覆盖来操作。模板的优先级是通过模板中的 order 字段定义的,数字越大,优先级越高

如下为定义所有以 te 开头的索引的模板:


{ "order": 0 "template" : "te*", "settings" : { "number_of_shards" : 1 }, "mappings" : { "type1" : { "_source" : { "enabled" : false } } }}
复制代码

索引模板是有序合并的。如何想单独修改某一小类索引的一两处单独设置,可以在累加一层模板:

{    "order" : 1,    "template" : "tete*",    "settings" : {        "number_of_shards" : 2    },    "mappings" : {        "type1" : {            "_all" : { "enabled" : false }        }    }}
复制代码

上述第一个模板的 order 为 0,第二个模板的 order 为 1,优先级高于第一个模板,其会覆盖第一个模板中的相同项。所以对于所有以 tete 开头的索引模板效果如下:

{    "settings" : {        "number_of_shards" : 2    },    "mappings" : {        "type1" : {            "_source" : { "enabled" : false },            "_all" : { "enabled" : false }        }    }}
复制代码

两个模板叠加了,项目的字段,优先级高的覆盖了优先级低的,如分片数。

2)索引模板的匹配

索引模板中的 "template" 字段定义的是该索引模板所应用的索引情况。如 "template": "tete*" 所表示的含义是,当新建索引时,所有以 tete 开头的索引都会自动匹配到该索引模板。利用该模板进行相应的设置和字段添加等。

3)setting 部分

索引模板中的 setting 部分一般定义的是索引的主分片、拷贝分片、刷新时间、自定义分析器等。常见的 setting 部分结构如下:

"settings": {    "index": {      "analysis": {...},                // 自定义的分析器      "number_of_shards": "32",         // 主分片的个数      "number_of_replicas": "1",        // 主分片的拷贝分片个数      "refresh_interval": "5s"          // 刷新时间    }  }
复制代码

八、冷热数据分离

ES 集群的索引写入及查询速度主要依赖于磁盘的 IO 速度,冷热数据分离的关键为使用 SSD 磁盘存储数据。若全部使用 SSD,成本过高,且存放冷数据较为浪费,因而使用普通 SATA 磁盘与 SSD 磁盘混搭,可做到资源充分利用,性能大幅提升的目标。为了解决控制成本的前提下读写性能问题,Elasticsearch 冷热分离架构应运而生。

  • 冷数据索引:查询频率低,基本无写入,一般为当天或最近 2 天以前的数据索引

  • 热数据索引:查询频率高,写入压力大,一般为当天数据索引


1)实现原理

  • Hot 节点设置:索引节点(写节点),同时保持近期频繁使用的索引。 属于 IO 和 CPU 密集型操作,建议使用 SSD 的磁盘类型,保持良好的写性能;节点的数量设置一般是大于等于 3 个。将节点设置为 hot 类型:

node.attr.box_type: hot
复制代码
  • Warm 节点设置: 用于不经常访问的 read-only 索引。由于不经常访问,一般使用普通的磁盘即可。内存、CPU 的配置跟 Hot 节点保持一致即可;节点数量一般也是大于等于 3 个。将节点设置为 warm 类型:

node.attr.box_type: warm
复制代码



es1:master 节点

# elasticsearch.ymlnode.name: "master"cluster.name: "test-cluster"network.host: 0.0.0.0node.master: truenode.data: false
复制代码

es2、es3、es4 热数据节点

# elasticsearch.ymlnode.name: "hot-datanode-00x" # 提示:自行修改其他节点的名称cluster.name: "test-cluster"network.host: 0.0.0.0node.master: falsenode.data: truediscovery.zen.ping.unicast.hosts: ["master"]node.attr.box_type: "hot"  # 标识为热数据节点
复制代码

es5、es6 冷/温数据节点

# elasticsearch.ymlnode.name: "cold-datanode-00x" # 提示:自行修改其他节点的名称cluster.name: "docker-cluster" network.host: 0.0.0.0 node.master: false node.data: true discovery.zen.ping.unicast.hosts: ["master"] node.attr.box_type: "warm" # 标识为温数据节点
复制代码

九、分片不均衡原因 &解决方案

原因

可能存在的部分原因有以下几种:

  • Shard 设置不合理。

说明:大多数负载不均问题是由于 shard 设置不合理导致,建议优先排查。

  • Segment 大小不均。

  • 存在典型的冷热数据需求场景。

说明:例如查询中添加了 routing 或查询频率较高的热点数据,则必然导致数据出现负载不均。

  • 没有释放长连接,导致流量不均。

说明:该问题时常暴露于采用负载均衡及多可用区架构部署时。

解决方案

1)方案一:手动移动分片

例如移动 node-1 的分片 0 到 node-4

curl -XPOST 'http://localhost:9200/_cluster/reroute' -d '{  "commands":[{  "move":{    "index":"indexName",    "shard":0,    "from_node":"node-1",    "to_node":"node-4"}}]}'
复制代码
  • 优点:操作简单,恢复时间短;不必修改 master node 的配置,master node 长期负载后高

  • 缺点:索引大,移动时有很高的 IO,索引容易损坏,需要做备份,不能解决 master node 既是数据节点又是负载均衡转发器的问题

【注意】分片和副本无法移动到同一个节点

2)方案二:重建索引,从另外一个集群导入

删除原来的索引,重新建立索引,;利用 elasticsearch dump 等工具从另一个集群中把数据导入到新的索引中

  • 优点:可以重新配置 master node 和 data node,主从负载均匀

  • 缺点:费时间,容易数据丢失,需要验证数据的一致性

3)方案三:配置平衡参数

使用下面的命令恢复平衡

PUT_cluster/settings{	"persistent": {		"cluster.routing.rebalance.enable": "all"	}}
复制代码

十、解决 Elasticsearch 分片未分配的问题

原因整体概述:

  • 出现这个问题的原因是原有分片未正常关闭和清理,所以当分片要重新分配回出问题节点的时候没有办法获得分片锁。

  • 这不会造成分片数据丢失,只需要重新触发一下分配。

unassigned 分片问题可能的原因如下:

  1. INDEX_CREATED: 由于创建索引的 API 导致未分配。

  2. CLUSTER_RECOVERED: 由于完全集群恢复导致未分配。

  3. INDEX_REOPENED: 由于打开 open 或关闭 close 一个索引导致未分配。

  4. DANGLING_INDEX_IMPORTED: 由于导入 dangling 索引的结果导致未分配。

  5. NEW_INDEX_RESTORED: 由于恢复到新索引导致未分配。

  6. EXISTING_INDEX_RESTORED: 由于恢复到已关闭的索引导致未分配。

  7. REPLICA_ADDED: 由于显式添加副本分片导致未分配。

  8. ALLOCATION_FAILED: 由于分片分配失败导致未分配。

  9. NODE_LEFT: 由于承载该分片的节点离开集群导致未分配。

  10. REINITIALIZED: 由于当分片从开始移动到初始化时导致未分配(例如,使用影子 shadow 副本分片)。

  11. REROUTE_CANCELLED: 作为显式取消重新路由命令的结果取消分配。

  12. REALLOCATED_REPLICA: 确定更好的副本位置被标定使用,导致现有的副本分配被取消,出现未分配。

解决方案如下:执行修复命令

POST /_cluster/reroute?retry_failed
复制代码

十一、ES 读写数据过程

1)ES 写入数据的过程


  • 客户端发送任何一个请求到任意一个 node,这个节点就成为协调节点(coordinate node)

  • 协调节点对 document(可以手动设置 doc id,也可以由系统分配)进行 hash 路由,将请求转发给对应的 node

  • node 上的 primary shard 处理请求,然后将数据同步到 replica node

  • 协调节点如果发现 primary shard 所在的 node 和所有的 replica shard 所对应的 node 都搞定之后,就会将请求返回给客户端

2)ES 读数据过程


可以通过 doc id 来查询,根据 doc id 进行 hash,判断当时写这个 document 时是分配到哪个 shard 上去了,然后就去那个 shard 上查询。

  • 客户端发送任何一个请求到任意一个 node,这个节点就成为协调节点(coordinate node)

  • 协调节点对 doc id 进行 hash 路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及所有的 replica shard 中随机选择一个,让读请求负载均衡

  • 接受请求的 node,返回 document 给协调节点

  • 协调节点再将数据返回给客户端

十二、在海量数据中提高效率的几个手段

  • filesystem cache:ES 的搜索引擎严重依赖底层的 filesystem cache,如果给 filesystem cache 更多的内存,尽量让内存可以容纳所有的 index segment file 索引数据文件

  • 数据预热:对于那些你觉得比较热的数据,即经常会有人访问的数据,最好做一个专门的缓存预热子系统,对于热数据,每隔一段时间,系统本身就提前访问一下,让数据进入 filesystem cache 里面去,这样下次访问的时候,性能会更好一些。

  • 冷热分离冷数据索引:查询频率低,基本无写入,一般为当天或最近 2 天以前的数据索引,这种数据可以存储在机械硬盘 HDD 中热数据索引:查询频率高,写入压力大,一般为当天的数据索引,这种数据可以存储在 SSD 中

  • document 的模型设计:不要在搜索的时候去执行各种复杂的操作,尽量在 document 模型设计和数据写入的时候就将复杂操作处理掉

  • 分页性能优化:翻页的时候,翻得越深,每个 shard 返回的数据越多,而且协调节点处理的时间越长,此时,要用 scroll,scroll 会一次性的生成所有数据的快照,然后每次翻页都是通过移动游标来完成

用户头像

喜欢就点个关注吧~ 2023-04-10 加入

持续更新Java

评论

发布
暂无评论
分布式实时搜索和分析引擎——Elasticsearch_Java_会踢球的程序源_InfoQ写作社区