ElasticSearch学习

ElasticSearch

ElasticSearch是一个基于Lucene的搜索服务器

•是一个分布式、高扩展、高实时的搜索与数据分析引擎

•基于RESTful web接口

•Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎

2.ElasticSearch的应用场景

ElasticSearch主要运用于海量数据的检索例如:

•搜索:海量数据的查询

•日志数据分析

•实时数据分析

先来说一声ES中的几个名词,方便我们后续用:
先看一张 MySQL 和 ElasticSearch 的概念对比图:

在这里插入图片描述

Index: 索引,可以理解为 MySQL 中的数据库。不同的是在 ES 7.0 之后,一个 Index 中只有一张表(一个 type)

Type: 对索引中 document 的分组。在 ES 6.x 中只允许每个 Index 中包含一个 Type。在 7.x 版本中将彻底移除 Type

Document: 即数据

分片-Shard

同一个 Index 会分成多个 shard 存到不同的节点(服务器)去。每个 shard 有自己的一个或多个备份(replica shard)。前者叫 primary shard,负责处理写入数据,之后再同步到它的 replica shard。

为什么要对索引分片呢?

一是可以支持横向扩展,把不同的 shard 放到不同的节点上,就可以实现单索引容量的扩展。

二是提高了并行访问速度。

注:这里的 primary shard 和 replica shard 的概念与 Redis 中的主从有一些相似。虽然是不同的技术,但是在底层的一些设计思想上总是有一些相似之处。

一个分片shard是一个最小级别的工作单元,es把一个完整的索引分成多个分片。
仅保存了索引中所有数据的一部分。 分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。
文档存储在分片中,并且在分片中被索引,但是程序不会直接与分片通信,而是与索引通信。
Replicas分片:代表索引副本。es可以设置多个索引的副本,副本的作用是提高了系统的容错性。
当某个节点的分片损坏或者丢失的时候可以从副本中恢复。
还可以提高查询搜索效率,es会自动对搜索请求进行负载均衡。

实列场景:

假设 IndexA 有2个分片,我们向 IndexA 中插入10条数据 (10个文档),那么这10条数据会尽可能平均的分为5条存储在第一个分片,剩下的5条会存储在另一个分片中。

节点-Node

服务器,分为 master 节点和其他节点;master 节点负责管理工作,比如维护索引元数据、负责切换 primary shard 和 replica shard 等。primary shard 所在节点宕机后,由 master 节点负责其选举并切换。

一个节点node就是一个ElasticSearch的实例。
集群中的一个节点会被选举为主节点master,它将临时管理集群级别的一些变更,譬如新建或者删除索引、增加或者移除节点等等。
主节点不参与文档级别的变更或者搜索,所以不会成为集群的瓶颈。
任何节点都可以成为主节点。
用户能够与集群中的任何节点通信,包括主节点。
每一个节点都知道文档存在于哪个节点上,可以转发请求到相应的节点上。

集群-Cluster

node 集群,由实际物理机,通过同一 cluster name 组成的,

一个集群cluster由一个或者多个节点组成,具有相同的cluster.name,协同工作,分项数据和负载。
当有新的节点加入或者删除了一个节点时,集群回感知到并能够平衡数据。
ElasticSearch中可以监控很多信息,有一个最重要的就是集群健康。
集群健康有三个状态:green(所有主要分片和复制分片都可用),yellow(所有主要分片可用,但不是所有复制分片都可用),red(不是所有的主要分片都可用)。

说ES就必须说倒排索引:

在搜索引擎中,每个文档都有一个对应的文档 ID,文档内容被表示为一系列关键词的集合。例如,文档 1 经过分词,提取了 20 个关键词,每个关键词都会记录它在文档中出现的次数和出现位置。

那么,倒排索引就是关键词到文档 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现了关键词。

举个栗子。

有以下文档:

DocIdDoc
1谷歌地图之父跳槽 Facebook
2谷歌地图之父加盟 Facebook
3谷歌地图创始人拉斯离开谷歌加盟 Facebook
4谷歌地图之父跳槽 Facebook 与 Wave 项目取消有关
5谷歌地图之父拉斯加盟社交网站 Facebook

对文档进行分词之后,得到以下倒排索引。

WordIdWordDocIds
1谷歌1,2,3,4,5
2地图1,2,3,4,5
3之父1,2,4,5
4跳槽1,4
5Facebook1,2,3,4,5
6加盟2,3,5
7创始人3
8拉斯3,5
9离开3
104

另外,实用的倒排索引还可以记录更多的信息,比如文档频率信息,表示在文档集合中有多少个文档包含某个单词。

那么,有了倒排索引,搜索引擎可以很方便地响应用户的查询。比如用户输入查询 Facebook,搜索系统查找倒排索引,从中读出包含这个单词的文档,这些文档就是提供给用户的搜索结果。

这一点明白了就知道ES的原理的,接下来我们说细节:

ES写入数据

(选择协调节点—>根据文件进行路由转发给对应的节点—>节点的主分片处理请求—>数据同步到副本分片—>返回响应)

客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node(协调节点)。
coordinating node 对 document 进行路由,将请求转发给对应的 node(有 primary shard)。
实际的 node 上的 primary shard 处理请求,然后将数据同步到 replica node。
coordinating node 如果发现 primary node 和所有 replica node 都搞定之后,就返回响应结果给客户端。

在这里插入图片描述

ES搜索数据过程

es 最强大的是做全文检索,就是比如你有三条数据

java真好玩儿啊
java好难学啊
j2ee特别牛
你根据 java 关键词来搜索,将包含 java的 document 给搜索出来。es 就会给你返回:java真好玩儿啊,java好难学啊。

(协调节点 —> 分片对应的主分片或者副本分片—>返回文件id给协调节点—>根据文件id去个节点拉取实际数据)

客户端发送请求到一个 coordinate node。
协调节点将搜索请求转发到所有的 shard 对应的 primary shard 或 replica shard,都可以。
query phase:每个 shard 将自己的搜索结果(其实就是一些 doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。
fetch phase:接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。

ES读取数据

可以通过 doc id 来查询,会根据 doc id 进行 hash,判断出来当时把 doc id 分配到了哪个 shard 上面去,从那个 shard 去查询。

(协调节点—对应节点—轮询查询主分片和副本-返回协调节点-返回客户端)

客户端发送请求到任意一个 node,成为 coordinate node。
coordinate node 对 doc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
接收请求的 node 返回 document 给 coordinate node。
coordinate node 返回 document 给客户端。

ES写数据底层原理
在这里插入图片描述

先写入内存 buffer,在 buffer 里的时候数据是搜索不到的;同时将数据写入 translog 日志文件。

如果 buffer 快满了,或者到一定时间,就会将内存 buffer 数据 refresh 到一个新的 segment file 中,但是此时数据不是直接进入 segment file 磁盘文件,而是先进入 os cache 。这个过程就是 refresh。

每隔 1 秒钟,es 将 buffer 中的数据写入一个新的 segment file,每秒钟会产生一个新的磁盘文件 segment file,这个 segment file 中就存储最近 1 秒内 buffer 中写入的数据。

但是如果 buffer 里面此时没有数据,那当然不会执行 refresh 操作,如果buffer里面有数据,默认 1 秒钟执行一次 refresh 操作,刷入一个新的 segment file 中。

操作系统里面,磁盘文件其实都有一个东西,叫做 os cache,即操作系统缓存,就是说数据写入磁盘文件之前,会先进入 os cache,先进入操作系统级别的一个内存缓存中去。只要 buffer 中的数据被 refresh 操作刷入 os cache中,这个数据就可以被搜索到了。

【为什么叫 es 是准实时的?】 NRT,全称 near real-time。默认是每隔 1 秒 refresh 一次的,所以 es 是准实时的,因为写入的数据 1 秒之后才能被看到。可以通过 es 的 restful api 或者 java api,手动执行一次 refresh 操作,就是手动将 buffer 中的数据刷入 os cache中,让数据立马就可以被搜索到。只要数据被输入 os cache 中,buffer 就会被清空了,因为不需要保留 buffer 了,数据在 translog 里面已经持久化到磁盘去一份了。

重复上面的步骤,新的数据不断进入 buffer 和 translog,不断将 buffer 数据写入一个又一个新的 segment file 中去,每次 refresh 完 buffer 清空,translog保留。随着这个过程推进,translog 会变得越来越大。当 translog 达到一定长度的时候,就会触发 commit 操作。

commit 操作发生第一步,就是将 buffer 中现有数据 refresh 到 os cache 中去,清空 buffer。然后,将一个 commit point 写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file,同时强行将 os cache 中目前所有的数据都 fsync 到磁盘文件中去。最后清空 现有 translog 日志文件,重启一个 translog,此时 commit 操作完成。

这个 commit 操作叫做 flush。默认 30 分钟自动执行一次 flush,但如果 translog 过大,也会触发 flush。flush 操作就对应着 commit 的全过程,我们可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。

translog 日志文件的作用是什么?你执行 commit 操作之前,数据要么是停留在 buffer 中,要么是停留在 os cache 中,无论是 buffer 还是 os cache 都是内存,一旦这台机器死了,内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 translog 中,一旦此时机器宕机,再次重启的时候,es 会自动读取 translog 日志文件中的数据,恢复到内存 buffer 和 os cache 中去。

translog 其实也是先写入 os cache 的,默认每隔 5 秒刷一次到磁盘中去,所以默认情况下,可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中,如果此时机器挂了,会丢失 5 秒钟的数据。但是这样性能比较好,最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘,但是性能会差很多。

其实 es 第一是准实时的,数据写入 1 秒后可以搜索到;可能会丢失数据的。有 5 秒的数据,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盘上,此时如果宕机,会导致 5 秒的数据丢失。

数据写入 segment file 之后,同时就建立好了倒排索引。

segment: 一个 index 是由多个 segment (段文件,也就是倒排索引)组成。

translog 文件的作用:机器宕机后重启,es 会自动读取 translog 日志文件,恢复数据到 buffer 和 os cache;translog 也是先写入 os cache,默认 5s 一次刷到磁盘上。所以,最多会丢失 5s 的数据。

commit point 文件中记录了所有的 segment 的信息。

当 segment 足够多时,可以使用 merge 操作合并段文件。

删除/更新数据底层原理
  如果是删除操作,commit 的时候会生成一个 .del 文件,里面将某个 doc 标识为 deleted 状态,那么搜索的时候根据 .del 文件就知道这个 doc 是否被删除了。

如果是更新操作,就是将原来的 doc 标识为 deleted 状态,然后新写入一条数据。

buffer 每次 refresh 一次,就会产生一个 segment file,所以默认情况下是 1 秒钟一个 segment file,这样下来 segment file 会越来越多,此时会定期执行 merge。每次 merge 的时候,会将多个 segment file 合并成一个,同时这里会将标识为 deleted 的 doc 给物理删除掉,然后将新的 segment file 写入磁盘,这里会写一个 commit point,标识所有新的 segment file,然后打开 segment file 供搜索使用,同时删除旧的 segment file。

   在上面我们提到,每 1s 会 refresh 一次,这样就生成一个 segment file。会定期执行 merge 操作来合并 segment file,此时,会将标识为 deleted 的 doc 给物理删除掉,然后将新的 segment file 写入磁盘,写一个 commit point 来表示所有新的 segment file,同时删除旧的 segment file.

逆向索引是不可更改的,一旦它被建立了,里面的数据就不会再进行更改。这样做就带来了以下几个好处:

  1.    没有必要给逆向索引加锁,因为不允许被更改,只有读操作,所以就不用考虑多线程导致互斥等问题。
    
  2.    索引一旦被加载到了缓存中,大部分访问操作都是对内存的读操作,省去了访问磁盘带来的io开销。
    
  3.    因为逆向索引的不可变性,所有基于该索引而产生的缓存也不需要更改,因为没有数据变更。
    
  4.    使用逆向索引可以压缩数据,减少磁盘io及对内存的消耗。
    

倒排顾名思义就是通过Value去找key,跟我们传统意义的根据key找value还不太一样。

举个例子,还是上面的数据,我们可以看到es会建立以下的索引:

Name 倒排索引

TermPosting List
haha1
xixi2
heihei3

Size倒排索引

TermPosting List
241
1[2,3]

Sex倒排索引

TermPosting List
Female2
Male[1,3]

大家可以看到所有的倒排所有都有Term和Posting List这两个概念,Posting list就是一个int的数组,存储了所有符合某个term的文档id。

怎么根据value找key呢?就比如我要找所有性别是男生的人,Sex的倒排索引的Posting list可以告诉我是id为1和3的人,那再通过Name的term我可以看到1的是人haha,3的人是heihei,依次类推找到所有信息。

Es的查询速度是非常快的,但是目前看来如果只是以Term的样子去查找并不快呀?是为什么呢?

这里就会引出接下来的两个概念,Term Dictionary和Term Index。

Term Dictionary:这个很好理解,我上面说过都是各种Term组成的,那为了查找Term方便,es把所有的Term都排序了,是二分法查找的。

在这里插入图片描述

Trem Index:这是为了优化Term Dictionary而存在的,大家想呀这么多Term光是排序了肯定也不行,想要快就得放到内存,但是es数据量级往往是很大的,那放在磁盘?磁盘的寻址又会很慢,那怎么去减少磁盘上的寻址开销呢?Term Index

其实就是跟新华字典一样,每个字母开头的是哪些,再按照拼音去排序。

在这里插入图片描述

这就是三者的关系,是一张很经典的图了

脑裂

脑裂问题其实在集群部署的机器上都是会存在的,假设现在es集群有两个节点,节点1是主节点对外提供服务,节点2是副本分片节点。

在这里插入图片描述

现在两个节点因为网络原因断联了,会发现什么?主节点发现自己是主节点继续对外提供服务,副本节点发现没有主节点了,选举自己是主节点,也对外提供服务了,因为主节点不可用他也是被迫当主节点的。
在这里插入图片描述
在这里插入图片描述

对于调用者来说,这是很难发现差别的,除非去对比数据,脑裂场景就是搜索一个词汇他有时候能搜出那个商品,有时候不能,因为请求打在不同的节点上了。

那正常我们会怎么解决呢?elasticsearch.yml中有个配置:discovery.zen.minimum_master_nodes 这个参数决定了在选主过程中需要有多少个节点通信,默认是1,设置的原则就是设置为 集群节点数量/2+1个。

如果你的集群是三个节点,那这个参数就设置为3/2+1=2个,那挂了一个,另外两个可以通信,所以可以选出一个主的,如果你集群是两个个节点,参数还是2,但是你发现挂了一个只有一个节点自己跟自己通信,就不会选主了。

但是这样也有弊端只有2个节点的时候,挂一个就相当于服务不可用了,所以大家要保证集群是三个以上是最好的。

Elasticsearch的选举算法基于 Bully 选举算法,简单的说,在 Bully 算法中,每个节点都有一个编号,只有编号最大的存活节点才能成为 master 节点。

Bully 算法的具体过程为:

当任何一个进程P发现 master 不响应请求时,它发起一次选举,选举过程如下:

(1)P进程向所有编号比它大的进程发送一个 election 消息;

(2)如果无人响应,则P获胜,成为 master;

(3)如果编号比它大的进程响应,则由响应者接管选举工作,P的工作完成。

任何一个时刻,一个进程只能从编号比它小的进程接受 election 消息,当消息到达时,接受者发送一个 OK 消息给发送者,表明它在运行,接管工作。

最终除了一个进程外,其他进程都放弃,那个进程就是新的协调者,随后协调者将获胜消息发送给其他所有进程,通知它们新的协调者诞生了。

下面演示一个分片示例

启用一个既没有数据,也没有索引的单一节点, 如下图

在这里插入图片描述

在空的单节点集群中上创建一个叫做 blogs 的索引,设置3个主分片和一组从分片(每个主分片有一个从分片对应):

PUT /blogs
{
“settings” : {
“number_of_shards” : 3,
“number_of_replicas” : 1
}
}

集群示例图如下: (此时集群健康状态为: yellow 三个从分片还没有被分配到节点上):

在这里插入图片描述

主分片(primary shards) 启动并且运行了,这时集群已经可以成功的处理任意请求,但是 从分片(replica shards) 没有完全被激活。事实上,当前这三个从分片都处于 unassigned(未分配)的状态,它们还未被分配到节点上。在同一个节点上保存相同的数据副本是没有必要的,如果这个节点故障了,就等同于所有的数据副本也丢失了。

启动第二个节点,配置第二个节点与第一个节点的 cluster.name 相同(./config/elasticsearch.yml文件中的配置),它就能自动发现并加入到第一个节点的集群中,如下图:
在这里插入图片描述

cluster-health 的状态为 green,这意味着所有的6个分片(三个主分片和三个从分片)都已激活,文档在主节点和从节点上都能被检索

随着应用需求的增长,启动第三个节点进行横向扩展,集群内会自动重组,如图:

在这里插入图片描述

在 Node 1 和 Node 2 中分别会有一个分片被移动到 Node 3 上,这样一来,每个节点上就都只有两个分片了。这意味着每个节点的硬件资源(CPU、RAM、I/O)被更少的分片共享,所以每个分片就会有更好的性能表现

一共有6个分片(3个主分片和3个从分片),因此最多可以扩展到6个节点,每个节点上有一个分片,这样每个分片都可以使用到所在节点100%的资源了

主分片的数量在索引创建的时候就已经指定了,实际上,这个数字定义了能存储到索引中的数据最大量(具体的数量取决于你的数据,硬件的使用情况)。例如,读请求——搜索或者文档恢复就可以由主分片或者从分片来执行,所以当你拥有更多份数据的时候,你就拥有了更大的吞吐量

从分片的数量可以在运行的集群中动态的调整,这样我们就可以根据实际需求扩展或者缩小规模。接下来,我们来增加一下从分片组的数量:

PUT /blogs/_settings { “number_of_replicas” : 2 }

现在 blogs 的索引总共有9个分片:3个主分片和6个从分片, 又会变成一个节点一个分片的状态了,最终得到了三倍搜索性能的三节点集群:

在这里插入图片描述

说明:仅仅是在同样数量的节点上增加从分片的数量是根本不能提高性能的,因为每个分片都有访问系统资源的权限。你需要升级硬件配置以提高吞吐量。

尝试一下,把第一个节点杀掉,我们的集群就会如下图所示:

在这里插入图片描述

被杀掉的节点是主节点,主分片 1 和 2 在我们杀掉 Node 1 后就丢失了,我们的索引在丢失主节点的时候是不能正常工作的。如果我们在这个时候检查集群健康状态,将会显示 red:存在不可用的主节点

而为了集群的正常工作必须需要一个主节点,所以首先进行的进程就是从各节点中选择了一个新的主节点:Node 2

新的主节点所完成的第一件事情就是将这些在 Node 2 和 Node 3 上的从分片提升为主分片,然后集群的健康状态就变回至 yellow。这个提升的进程是瞬间完成了,就好像按了一下开关

如果再次杀掉 Node 2 的时候,我们的程序依旧可以在没有丢失任何数据的情况下运行,因为 Node 3 中依旧拥有每个分片的备份

如果我们重启 Node 1,集群就能够重新分配丢失的从分片,这样结果就会与三节点两从集群一致。如果 Node 1 依旧还有旧节点的内容,系统会尝试重新利用他们,并只会复制在故障期间的变更数据。

ElasticSearch怎样设置 master、data 和 client 节点
1、默认情况
在生产环境下,如果不修改elasticsearch节点的角色信息,在高数据量,高并发的场景下集群容易出现脑裂等问题。
默认情况下,elasticsearch 集群中每个节点都有成为主节点的资格,也都存储数据,还可以提供查询服务。这些功能是由两个属性控制的。

1)node.master
node.master:这个属性表示节点是否具有成为主节点的资格
注意:此属性的值为 true,并不意味着这个节点就是主节点。
因为真正的主节点,是由多个具有主节点资格的节点进行选举产生的。
所以,这个属性只是代表这个节点是不是具有主节点选举资格。
2)node.data
默认情况下这两个属性的值都是true。
node.data:这个属性表示节点是否存储数据。
2、四种组合
1) node.master: true AND node.data: true AND node.ingest: true
  这种组合表示这个节点既有成为主节点的资格,又可以存储数据,还可以作为预处理节点,
这个时候如果某个节点被选举成为了真正的主节点,那么他还要存储数据,这样对于这个节点的压力就比较大了。
  elasticsearch 默认是:每个节点都是这样的配置,在测试环境下这样做没问题。
实际工作中建议不要这样设置,这样相当于 主节点 和 数据节点 的角色混合到一块了。

2)node.master: false AND node.data: true AND node.ingest: false
  这种组合表示这个节点没有成为主节点的资格,也就不参与选举,只会存储数据。
这个节点我们称为 data(数据)节点。
在集群中需要单独设置几个这样的节点负责存储数据。后期提供存储和查询服务

3) node.master: true AND node.data: false AND node.ingest: false
  这种组合表示这个节点不会存储数据,有成为主节点的资格,可以参与选举,有可能成为真正的主节点。
这个节点我们称为master节点

4)node.master: false AND node.data: false AND node.ingest: true
  这种组合表示这个节点即不会成为主节点,也不会存储数据,
这个节点的意义是作为一个 client(客户端)节点,主要是针对海量请求的时候可以进行负载均衡。
在新版 ElasticSearch5.x 之后该节点称之为:coordinate 节点,其中还增加了一个叫:ingest 节点,用于预处理数据(索引和搜索阶段都可以用到),
当然,作为一般应用是不需要这个预处理节点做什么额外的预处理过程,那么这个节点和我们称之为 client 节点之间可以看做是等同的,
我们在代码中配置访问节点就都可以配置这些 ingest 节点即可。

3、总结
  默认情况下,每个节点都有成为主节点的资格,也会存储数据,还会处理客户端的请求。在一个生产集群中我们可以对这些节点的职责进行划分。

1)master节点
建议集群中设置 3台 以上的节点作为
master 节点【node.master: true node.data: false node.ingest:false】,
这些节点只负责成为主节点,维护整个集群的状态。

2)data节点
再根据数据量设置一批 data节点【node.master: false node.data: true node.ingest:false】,
这些节点只负责存储数据,后期提供建立索引和查询索引的服务,这样的话如果用户请求比较频繁,这些节点的压力也会比较大

3)client节点
所以在集群中建议再设置一批 ingest 节点也称之为 client 节点【node.master: false node.data: false node.ingest:true】,
这些节点只负责处理用户请求,实现请求转发,负载均衡等功能。

master节点:普通服务器即可(CPU 内存 消耗一般)
data 节点:主要消耗磁盘,内存
client | ingest 节点:普通服务器即可(如果要进行分组聚合操作的话,建议这个节点内存也分配多一点)

最后案例一个我参考许多大佬集成的一个ES的小demo,包括了ES的部署和使用:

https://github.com/Feker1998/elasticsearch

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值