Elastic Search工作原理

一、架构设计

ElasticSearch 设计的理念就是分布式搜索引擎,底层其实还是基于 lucene 的。核心思想就是在多台机器上启动多个 ES 进程实例,组成了一个 ES 集群。ES 中存储数据的基本单位是索引index,其次还包含一些其他的概念mapping、document、field。

类比一下MySQL,index 相当于 mysql 里的一张表。mapping表示index的结构定义,相当于在 mysql 中创建一个表,肯定是要定义表结构的,里面有哪些字段,每个字段是什么类型。document相当于是MySQL表中一行数据。每个 field 就代表了这个 document 中的一个字段的值。


分片是什么?

简单来讲就是咱们在ES中所有数据的文件块,也是数据的最小单元块,整个ES集群的核心就是对所有分片的分布、索引、负载、路由等达到惊人的速度

Shard 分片

分片本质上对应的Lucene的index,分片的引入其实采用分而治之的思想。单台机器的处理能力、内存、存储资源有限。在大数据时代,index的数据可能会增长快,远远超过单机的资源能力。将index的数据切分为shard分布到多个节点上,请求会发送到多个shard进行查询和合并结果。既实现了es的横向扩展能力,也提高了搜索性能。

本身是一个完整的搜索引擎,可理解为是一个Lucene实例,一个ES Index可分为多个Shard,分布到不同节点上,分担压力。

Shard由多个segment和一个commit point(记录当前已提交的segment)文件组成。

Segment

解释一:Lucene里面的一个数据集概念,每一个segment都是完整独立的索引,能被搜索,是es可以近实时搜索的主要原因,倒排索引在一创建时就无法改变,因为改变索引结构代价较大,es解决方法就是创建新segment来反映修改,查询时会从旧segment到新的依次查询并且在返回时合并结果,将整个过程无感化,真正的修改是在mege Segment步骤时进行的。

解释二:

segment是Lucene的一个概念,不过Lucene的Index由多个segment和提交点(commit point)组成,主要用于不需要进行提交可以让文件被检索。这里简单说明下:

在es和磁盘之间存在一个文件系统缓存层,

in-memory indexing buffer的文档会待写入一个新的segment。如下图所示:


in-memory indexing buffer的文档会写入一个新的segment后(灰色标注的数据),因为segment本身是可被检索的,从而实现了不用commit(刷到硬盘),就可检索的效果,如下图所示:

每个segment会生成一个segment文件,该文件是不可变的。涉及到文档更新均是先逻辑删除,再在merge阶段进行物理删除。

总结

到了这里,本文简单讲述了es的索引、shard、Lucene的索引以及Segment,为了更方便理解它们的关系,画个二维表格协助理解:

es的index包含多shard,shard对应的是Lucene的一个index,Lucene的index包含多个segment。


ES 集群多个节点,会自动选举一个节点为 master 节点,这个 master 节点其实就是干一些管理的工作的,比如维护索引元数据、负责切换 primary shard 和 replica shard 身份等。要是 master 节点宕机了,那么会重新选举一个节点为 master 节点。

如果是非 master 节点宕机了,那么会由 master 节点,让那个宕机节点上的 primary shard 的身份转移到其他机器上的 replica shard。接着你要是修复了那个宕机机器,重启了之后,master 节点会控制将缺失的 replica shard 分配过去,同步后续修改的数据之类的,让集群恢复正常。

说得更简单一点,就是说如果某个非 master 节点宕机了。那么此节点上的 primary shard 不就没了。那好,master 会让 primary shard 对应的 replica shard(在其他机器上)切换为 primary shard。如果宕机的机器修复了,修复后的节点也不再是 primary shard,而是 replica shard。


工作原理

二、工作流程

1.ES写数据过程

客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node (协调节点)。

coordinating node 对 document 进行路由,将请求转发给对应的 node(有 primary shard)。

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

coordinating node 如果发现 primary node 和所有 replica node 都搞定之后,就返回响应结果给客户端。

2.ES搜索数据过程

客户端发送请求到一个 coordinate node 。

协调节点将搜索请求转发到所有的 shard 对应的 primary shard 或 replica shard ,都可以。

query phase:每个 shard 将自己的搜索结果(其实就是一些 doc id )返回给协调节点,由协调节点进行数据的合并、排序、分页等操作,产出最终结果。

fetch phase:接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据,最终返回给客户端。

3.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 给客户端。

三、写数据底层原理

写数据原理

先写入内存 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

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 到磁盘,但是性能会差很多。

总结一下,数据先写入内存 buffer,然后每隔 1s,将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到(所以我们才说 es 从写入到能被搜索到,中间有 1s 的延迟)。每隔 5s,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30min,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。

四、倒排索引

我们先来看看MySQL中的索引,在MySQL关系表中,每一行数据都有一个主键,我们在查询数据的时候会先找到数据的主键,再根据主键去找到整行数据,这种索引形式成为正排索引,本质是通过key来查询value。而ES使用的倒排索引正好与之相反,是通过对value进行分词,然后根据关键词通过value去查询key。

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


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

要注意倒排索引的两个重要细节:

倒排索引中的所有词项对应一个或多个文档。

倒排索引中的词项根据字典顺序升序排列。(根据实际需要)

五、ES为什么查询效率很高

1.倒排索引

上面介绍了ES的倒排索引的基本逻辑,基于这种数据结构,对于用户任意关键词的搜索,都能快速获取到文档id集合,然后再根据文档id集合去节点上面获取整个文档数据。

2.单词词典

上面说了在创建倒排索引的时候ES会进行分词,一个文档在分词之后可能会产生很多独立的单词,而对于这么多的单词,我们该如何找到我们需要的关键词呢,只需要将所有的单词采用二叉搜索树的数据结构进行排列,这样就可以高效的搜索出我们需要的关键词。但是,这样也有一个问题,如果拆分出的单词非常多,那么生成的二叉搜索树层数会很高,全部放在硬盘上面随即遍历的时候IO开销很大,全部加载到内存十分占用内存,为了解决这个问题,ES又引出了下面单词索引的设计。

3.单词索引

举个例子:现在有一份学生信息的数据,对姓名字段分词之后包含如下单词:tom、jerry、felix、andy等上千万个单词组成一个巨大的单词词典,很显然千万层级的二叉树是很难全部放在内存中遍历的,因此针对这种情况,设计出了单词索引。单词索引类似于字典的目录页:以a开头的单词在xx页,以b开头的单词在xx页······,记录着单词中每个次的前缀和对应的单词的物理地址,数据结构是参考了MySQL的主键数据结构,也是采用的B+树,极大的提高了搜索的性能。这样一来,我们在搜索关键词时,会先查单词索引,根据前缀匹配到所有的单词物理地址,再通过单词词典查询到所有的倒排索引集合,最后将所有的docId进行一系列筛选后去对应的节点获取数据。

4.位图BitMap

除了上述的三种设计上优化外,针对大数据量的搜索,ES也使用了专门用来针对大数据量的数据结构BitMap,在拿到所有的倒排索引后,获取的docId可能还是非常多,而且肯定还存在很多重复的docId,要去重合并甚至还要排序之后才能去节点拿取数据,这样才能最大限度的减少IO的开销。拿到倒排索引后可以根据docId来创建对应的BitMap,比如最后得到的倒排索引如下:

[1, 3, 5] => 10101

[1, 2, 4, 5] => 11011

[1, 2, 5] => 10011

将对应的BitMap按位与操作之后得到

10001 => [1, 5]

因此最后我们实际需要查询的docId其实只有两个:[1, 5] ,最终只需要两次IO即可获取查询结果。



ES各名词解释

索引(Index)

ES将数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库,或者一个数据存储方案(schema)。索引由其名称(必须为全小写字符)进行标识,并通过引用此名称完成文档的创建、搜索、更新及删除操作。一个ES集群中可以按需创建任意数目的索引。

类型(Type)

类型是索引内部的逻辑分区(category/partition),其意义完全取决于用户需求。因此,一个索引内部可定义一个或多个类型(type)。一般来说,类型就是为那些拥有相同的域的文档做的预定义。例如,在索引中,可以定义一个用于存储用户数据的类型,一个存储日志数据的类型,以及一个存储评论数据的类型。类比传统的关系型数据库领域来说,类型相当于 “表” 。

Elasticsearch 为何要在 7.X版本中 去除type的概念

背景说明

Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。

           Elasticsearch 是一种NoSQL数据库(非关系型数据库),和常规的关系型数据库(比如:MySQL,Oralce等)的基本概念,对应关系如下:



Elasticsearch:index-->type --> doc--> field

MySQL:           数据库  -->   数据表 -->    行     -->  列

因为关系型数据库比非关系型数据库的概念提出的早,而且很成熟,应用广泛。

所以,后来很多NoSQL(包括:MongoDB,Elasticsearch等)都参考并延用了传统关系型数据库的基本概念。

一个客观的现象和事实如下:

Elasticsearch 官网提出的近期版本对 type 概念的演变情况如下:

5.X版本中,一个 index可以创建多个 type

6.X版本中,一个 index只能存在一个 type

7.X版本中,直接去除了 type的概念,就是说index 不再会有 type

原因

因为 Elasticsearch 设计初期,是直接查考了关系型数据库的设计模式,存在了 type(数据表)的概念。

但是,其搜索引擎基于 Lucene的,这种 “基因” 决定了 type 是多余的。 Lucene 的全文检索功能之所以快,是因为倒排索引的存在。

而这种 倒排索引 的生成是基于 index 的,而并非 type。多个type 反而会减慢搜索的速度。

为了保持 Elasticsearch “一切为了搜索” 的宗旨,适当的做些改变(去除 type)也是无可厚非的,也是值得的。

为何不是在 6.X 版本开始就直接去除 type,而是要逐步去除type?

因为历史原因,前期 Elasticsearch 支持一个 index 下存在多个 type的,而且,有很多项目在使用 Elasticsearch 作为数据库。

如果直接去除 type 的概念,不仅是很多应用 Elasticsearch 的项目面临 业务、功能和代码的大改

而且对于Elasticsearch 官方来说,也是一个巨大的挑战(这个是伤筋动骨的大手术,很多涉及到 type 源码是要修改的)。

所以,权衡利弊,采取逐步过渡的方式,最终,推迟到 7.X 版本才完成 “去除 type”  这个革命性的变革



文档(Document)

文档是索引和搜索的原子单位,它是包含了一个或多个域(Field)的容器,基于JSON格式进行表示。文档由一个或多个域组成,每个域拥有一个名字及一个或多个值,有多个值的域通常称为 “多值域” 。每个文档可以存储不同的域集,但同一类型下的文档至应该有某种程度上的相似之处。

document路由原理

①路由算法:shard = hash(routing) % number_of_primary_shards

②决定一个document在哪个shard上,最重要的一个值就是routing值,默认是_id,也可手动指定,相同的routing值,每次过来,从hash函数中,产出的hash值一定是相同的

例:手动指定一个routing value,比如 put /index/type/id?routing=user_id

③这就是primary shard数量不可变的原因。

节点(Node)

一个运行中的 Elasticsearch 实例称为一个节点,而集群是由一个或者多个拥有相同cluster.name 配置的节点组成, 它们共同承担数据和负载的压力。

ES集群中的节点有三种不同的类型:

主节点:负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 主节点并不需要涉及到文档级别的变更和搜索等操作。可以通过属性node.master进行设置。

数据节点:存储数据和其对应的倒排索引。默认每一个节点都是数据节点(包括主节点),可以通过node.data属性进行设置。

协调节点:如果node.master和node.data属性均为false,则此节点称为协调节点,用来响应客户请求,均衡每个节点的负载。

分片(Shard)

一个索引中的数据保存在多个分片中,相当于水平分表。一个分片便是一个Lucene 的实例,它本身就是一个完整的搜索引擎。我们的文档被存储和索引到分片内,但是应用程序是直接与索引而不是与分片进行交互。

ES实际上就是利用分片来实现分布式。分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。 当你的集群规模扩大或者缩小时, ES会自动的在各节点中迁移分片,使得数据仍然均匀分布在集群里。

一个分片可以是主分片或者副本分片。 索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。一个副本分片只是一个主分片的拷贝。副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。

如果当前插入大量数据,那么会对es集群造成一定的压力,所以在插入大量数据前,也就是在建立索引的时候,我们最好把副本数设置为0;等数据建立完索引之后,在手动的将副本数更改到2,这样可以提高数据的索引效率

在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改。默认情况下,一个索引会有5个主分片,而其副本可以有任意数量。

主分片和副本分片的状态决定了集群的健康状态。每一个节点上都只会保存主分片或者其对应的一个副本分片,相同的副本分片不会存在于同一个节点中。如果集群中只有一个节点,则副本分片将不会被分配,此时集群健康状态为yellow,存在丢失数据的风险。

实际上,每一个分片还会进一步拆分为分段(Segment)。这是ES写入文档所采用的机制决定的。

版权声明:本文为CSDN博主「Super_Hero1」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_41651503/article/details/10945945

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容