ElasticSearch基本概念

1、核心概念

ElasticSearch基本概念_第1张图片

  • 索引index:数据库
  • doc_type:table
  • mapping:db_schema(有点类似于表的定义)
  • primary shard(主分片)
  • replica shard(副分片)

2、数据组织

2.1 逻辑组织

Elasticsearch同样具有这样的概念,如下图所示,使用index和doc_type来组织数据。doc_type中的每条数据称为一个document,是一个JSON Object,相关的schema信息通过mapping来定义。mapping不仅仅包括数据类型的定义,还有很多其他元信息的设置,它们共同决定了数据如何被存储和索引。这四个概念实现了Elasticsearch的逻辑数据组织,假设有一批结构化或半结构化数据需要存储,我们会先对数据进行分类,设计相应的index与doc_type,再为每个doc_type设置相关的mapping信息。如果不指定mapping,Elasticsearch会使用默认值,并自动为你推导每个字段的类型,即支持schema free的特性。但是,这种灵活性也会带来一些问题,一方面会失去对数据的控制,即会越来越不清楚你的数据结构,另一方面,自动推导出来数据类型可能不是预期的,会带来写入和查询问题。所以,笔者建议,尽最大可能对schema加以约束。

ElasticSearch基本概念_第2张图片

通常情况下,我们会拿elastisearch的概念和关系型数据库进行对比,比如idex等价于database,doc_type等价于table,mapping等价于db_schema。需要注意的是,关系型数据库的表之间是完全独立的,不同table的schema(定义)是完全隔离的,而elasticearch得doc_type则不是,同一个index下的不同doc_type中的字段在底层是合在一起存储的,意味假设两个doc_type都有一个name字段,那么这两个字段的mapping必须一样。基于这个原因,Elasticsearch官方从6.0开始淡化doc_type的概念,推荐一个index只拥有一个doc_type,并计划在8.x完全废弃doc_type。因此,在当前的index设计中,最好能遵循这个规则。

2.1 物理组织

  • 分布式存储:每个index得数据会划分到不同的分片,这些分片会分配到不同的节点上进行存储,可以突破单机在存储空间和处理性能的限制。
  • 冗余:分布式的前提是接收系统中某个节点因为某些故障退出。为了保证在故障节点推出后数据不丢失,同一份数据需要拷贝多份存在不同节点。因此,分片从角色上划分为primary shard和replica shard。数据首先写入primary shard,再同步到replica shard。
    ElasticSearch基本概念_第3张图片
    shard是Elasticsearch中最小的数据分配单位,即一个shard总是作为一个整体被分配到某个节点,而不会只分配其中一部分。那么,shard中的数据又是如何组织的?答案是segment。一个shard包含一组segment,segment是最小的数据单元,Elasticsearch每隔一段时间产生一个新的segment,里面包含了新写入的数据。segment是immutable的,即不可改变,这样设计的考量是:一方面,不支持修改就不用对读写操作加锁,省去了相关开销;另一方面,因为文件内容不会修改,可以更好的利用filesystem cache进行缓存,提高查询性能。但是,任何设计都不是完美的,伴随而来的问题是:如果segment不可修改,怎么实现数据的更新与删除呢?这个问题将在下面“数据写入”一节介绍。

2.2 数据分布

  上面提到,Elasticsearch将每个index中的数据划分到不同的shard中,然后将shard分配到不同的节点上,实现分布式存储。这里面涉及到两个概念:一个是数据到shard的映射(route),另一个是shard到节点的映射(shard allocate)。

一方面,插入一条数据时,Elasticsearch会根据指定的key来计算应该落到哪个shard上。默认key是自动分配的id,可以自定义,比如在我们的业务中采用CompanyID作为key。因为primary shard的个数是不允许改变的,所以同一个key每次算出来的shard是一样的,从而保证了准确定位。

shard_num = hash(_routing) % num_primary_shards

另一方面,master节点会为每个shard分配相应的data节点进行存储,并维护相关元信息。通过route计算出来的shard序号,在元信息中找到对应的存储节点,便可完成数据分布。shard allocate的映射关系并不是完全不变的,当检测到数据分布不均匀、有新节点加入或者有节点挂掉等情况时就会进行调整,称为relocate。
  关于数据分布,可以参考阅读博文《谈Elasticsearch下分布式存储的数据分布》。
  ElasticSearch基本概念_第4张图片

2.3 集群角色

  • 一个分布式系统,是由多个节点各司其职、相互协作完成整体服务的,从架构上可以分为有中心管理节点和无中心管理节点两种,Elasticsearch属于前者。中心管理节点负责维护整个系统的状态和元信息,为了保证高可用性,通常是从一组候选节点中选举出来的,而非直接指定。
  • 一个运行中的 Elasticsearch 实例称为一个节点,而集群是由一个或者多个拥有相同cluster.name配置的节点组成, 它们共同承担数据和负载的压力。
  • ES集群中的节点有三种不同的类型:
    • 主节点:负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 主节点并不需要涉及到文档级别的变更和搜索等操作。可以通过属性node.master进行设置。
    • 数据节点:存储数据和其对应的倒排索引。默认每一个节点都是数据节点(包括主节点),可以通过node.data属性进行设置。
    • 协调节点:如果node.master和node.data属性均为false,则此节点称为协调节点,用来响应客户请求,均衡每个节点的负载。

Elasticsearch以REST API形式对外提供服务,数据写入与查询都会发送HTTP(S)请求到服务端,由负载均衡将请求分发到集群中的某个节点上(任何非dedicated master-eligible节点)。如下图所示,节点1收到请求后,会根据相关的元信息将请求分发到shard所在的节点(2和3)上进行处理,处理完成后,节点2和3会将结果返回给节点1,由节点1合并整理后返回给客户端。这里的节点1扮演着协调者的角色,称为coordinate节点,任何节点在收到请求后就开始发挥协调者的角色,直到请求结束。在实际使用中,可以根据需要增加一些专用的coordinate节点,用于性能调优。
ElasticSearch基本概念_第5张图片

2.4 数据写入:针对文档的CRUD操作

索引新文档(create)

  • 当有数据写入时,请求会先到达集群中的某个节点上,由该节点根据routing信息和元信息将相应的数据分发到对应的shard所在的节点上,可能是一个也可能是多个节点,取决于写入的数据。因此协调节点会将请求发送给对应的节点。注意这个请求会发送给主分片,等主分片完成索引,会并行将请求发送到其所有副本分片,保证每个分片都持有最新数据。
  • 每次写入新文档时,都会先写入内存中,并将这一操作写入一个translog文件(transaction log)中,此时如果执行搜索操作,这个新文档还不能被索引到。

ElasticSearch基本概念_第6张图片
图5、新文档被写入内存,操作被写入translog

  • ES会每隔1秒时间(这个时间可以修改)进行一次刷新操作(refresh),此时在这1秒时间内写入内存的新文档都会被写入一个文件系统缓存(filesystem cache)中,并构成一个分段(segment)。此时这个segment里的文档可以被搜索到,但是尚未写入硬盘,即如果此时发生断电,则这些文档可能会丢失。
    ElasticSearch基本概念_第7张图片
    图6、在执行刷新后清空内存,新文档写入文件系统缓存
  • 不断有新的文档写入,则这一过程将不断重复执行。每隔一秒将生成一个新的segment,而translog文件将越来越大。
    ElasticSearch基本概念_第8张图片
    图7、translog不断加入新文档记录
  • 每隔30分钟或者translog文件变得很大,则执行一次fsync操作。此时所有在文件系统缓存中的segment将被写入磁盘,而translog将被删除(此后会生成新的translog)。
    ElasticSearch基本概念_第9张图片
    图8、执行fsync后segment写入磁盘,清空内存和translog
  • 由上面的流程可以看出,在两次fsync操作之间,存储在内存和文件系统缓存中的文档是不安全的,一旦出现断电这些文档就会丢失。所以ES引入了translog来记录两次fsync之间所有的操作,这样机器从故障中恢复或者重新启动,ES便可以根据translog进行还原。
  • 当然,translog本身也是文件,存在于内存当中,如果发生断电一样会丢失。因此,ES会在每隔5秒时间或是一次写入请求完成后将translog写入磁盘。可以认为一个对文档的操作一旦写入磁盘便是安全的可以复原的,因此只有在当前操作记录被写入磁盘,ES才会将操作成功的结果返回发送此操作请求的客户端。
  • 此外,由于每一秒就会生成一个新的segment,很快将会有大量的segment。对于一个分片进行查询请求,将会轮流查询分片中的所有segment,这将降低搜索的效率。因此ES会自动启动合并segment的工作,将一部分相似大小的segment合并成一个新的大segment。合并的过程实际上是创建了一个新的segment,当新segment被写入磁盘,所有被合并的旧segment被清除。
    ElasticSearch基本概念_第10张图片
    图9、合并segment
    ElasticSearch基本概念_第11张图片
    图10、合并完成后删除旧segment,新segment可供搜索
    更新和删除文档
  • ES的索引是不能修改的,因此更新和删除操作并不是直接在原索引上直接执行。
  • 每一个磁盘上的segment都会维护一个del文件,用来记录被删除的文件。每当用户提出一个删除请求,文档并没有被真正删除,索引也没有发生改变,而是在del文件中标记该文档已被删除。因此,被删除的文档依然可以被检索到,只是在返回检索结果时被过滤掉了。每次在启动segment合并工作时,那些被标记为删除的文档才会被真正删除。
  • 更新文档会首先查找原文档,得到该文档的版本号。然后将修改后的文档写入内存,此过程与写入一个新文档相同。同时,旧版本文档被标记为删除,同理,该文档可以被搜索到,只是最终被过滤掉。
    读操作:查询
  • 查询的过程大体上分为查询(query)和取回(fetch)两个阶段。这个节点的任务是广播查询请求到所有相关分片,并将它们的响应整合成全局排序后的结果集合,这个结果集合会返回给客户端。
    查询阶段
    当一个节点接收到一个搜索请求,则这个节点就变成了协调节点。
    ElasticSearch基本概念_第12张图片
    图10、查询过程分布式搜索
  • 第一步是广播请求到索引中每一个节点的分片拷贝。 查询请求可以被某个主分片或某个副本分片处理,协调节点将在之后的请求中轮询所有的分片拷贝来分摊负载。
  • 每个分片将会在本地构建一个优先级队列。如果客户端要求返回结果排序中从第from名开始的数量为size的结果集,则每个节点都需要生成一个from+size大小的结果集,因此优先级队列的大小也是from+size。分片仅会返回一个轻量级的结果给协调节点,包含结果集中的每一个文档的ID和进行排序所需要的信息。
  • 协调节点会将所有分片的结果汇总,并进行全局排序,得到最终的查询排序结果。此时查询阶段结束。
    取回阶段
  • 查询过程得到的是一个排序结果,标记出哪些文档是符合搜索要求的,此时仍然需要获取这些文档返回客户端。
  • 协调节点会确定实际需要返回的文档,并向含有该文档的分片发送get请求;分片获取文档返回给协调节点;协调节点将结果返回给客户端。
    ElasticSearch基本概念_第13张图片
    相关性计算
  • 在搜索过程中对文档进行排序,需要对每一个文档进行打分,判别文档与搜索条件的相关程度。在旧版本的ES中默认采用TF/IDF(term frequency/inverse document frequency)算法对文档进行打分。
  • TF叫做词频,指一篇文章中某个Term出现的次数,为了防止某个文件太大,一般会进行归一化。TF=(某文档中出现的次数)/文档的总词数。
  • 如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
  • 这样想的话我们可以构造一个公式用来衡量权重,假设令DF=(包含该Term的文档个数)/总文档数。则首先想到的是TF/DF,即我们希望TF越大或者DF越小,则权重越大。
  • 想到DF可能会取一个比较大的值,因此对DF取对数。即得到TF*log(总文档数/包含该Term的文档个数),由于包含该Term的文档个数可能为0,因此最终得到的公式是:TF*log(总文档数/(包含该Term的文档个数+1)).

数值类型对搜索效率的影响

  • ES5.x之前用到的Lucene版本,实际上只能够索引文本类型的数据,表面上被定义为数值类型的字段,在暗地里都被转换成了字符串,编排成了倒排索引。

2.5 存储结构

  • 在讲存储结构之前,先来看看两种常见的查询需求(以一组博文信息数据为例,有作者、标题等信息)。一种是精确匹配,比如查找作者姓名为"Bruce"的信息;另一种是全文检索,比如从1000个文章的标题中搜索出包含"分布式"的文章。对于第一个需求,我们只需要将每个名字作为一个term即可,“是"或"不是”;对于第二个,我们如果想知道标题中是否包含"分布式",就需要提前将每个标题分解为多个term,比如"浅谈分布式存储系统",可能会产生"浅谈"、“分布式”、“存储”、"系统"等多个term,具体取决于使用了哪一种分析器。
  • 不管哪种情况,最后都是产生一组term,问题是用一个什么样的存储结构可以实现快速检索。这就是Elasticsearch的核心:inverted index。inverted index是一个二维结构,如下所示,包含一组排好序的term,每个term都关联有一些信息,这些信息指出哪些document包含了这个term。当需要查询包含关键词"分布式"的数据时,系统会先从inverted index中找出对应的term,获取到其对应的document id,然后就可以根据document id找出其信息了。
sample data:
1. {"author": "Bruce", "title": "浅谈分布式存储系统"}
2. {"author": "Bruce", "title": "常见的分布式系统"}
3. {"author": "David", "title": "分布式存储原理"}
inverted index for field "author":
-------------------------------
term     |   doc id
-------------------------------
Bruce    |   1, 2
David    |   3
-------------------------------

inverted index for field "title":
 -------------------------------
term     |   doc id
-------------------------------
常见      |   3
存储      |   1, 3
分布式    |   1, 2, 3
浅谈      |   1
系统      |   1, 2
原理      |   3
-------------------------------

参考:https://blog.csdn.net/zwgdft/article/details/83619905
https://blog.csdn.net/zkyfcx/article/details/79998197

你可能感兴趣的:(ElasticSearch)