导语
这篇文章中主要分享关于ElasticSearch相关的基础知识,介绍一下关于ElasticSearch相关的术语以及它的架构相关的内容
文章目录
ElasticSearch 简介
ElasticSearch 是一个分布式、可扩展、实时性挺高的一个高性能的搜索引擎,基于Apache Lucene 构建,采用Java语言编写,使用了Lucene构建的搜索功能以及索引功能。让全文检索功能变得相对简单。
主要优势如下
- 1、分布式实时文件存储。ElasticSearch 可以将被索引的文档中的每一个字段保存到索引中,方便检索使用。
- 2、实时分析的分布式搜索引擎。ElasticSearch 的索引拆分成多个分片,每个分片都有不同数量的副本。集群中的每个节点都保留一个或者多个分片,同时协调处理多个操作,负载均衡和路由会自动进行匹配。
- 3、高可拓展性。在大规模的应用方面,ElasticSearch 可以扩展到上百台机器。处理PB级别的结构化或者非结构化数据,当然既然可以多机器部署,那么就可以在一台机器上部署。
- 4、可插拔的插件。ElasticSearch 支持多种插件,例如分词插件、同步插件、Hadoop插件、可视化插件、例如ELK的使用。
根据最新的数据库引擎排行榜,ElasticSearch、Splunk、Solr等位列前三。
ElasticSearch 的核心概念
ElasticSearch的核心概念有Node、Cluster、Shards、Replicas、Index、Type、Document、Settings、Mapping和Analyzer。下面就来分别介绍一下这些内容
- 1、Node:节点,节点是组成ElasticSearch集群的基本服务单元,集群中的每个运行中的ElasticSearch服务器都可以称之为节点。
- 2、Cluster:集群,ElasticSearch 的集群是由相同的cluster.name的一个或者多个ElasticSearch节点组成,各个节点协同工作,共享数据。同一个集群节点的名字不能重复,但集群名字一定要相同。
在实际的开发中需要给ElasticSearch集群起一个合适的名字,来代替cluster.name的默认值,自定集群名称有利于管理。
在ElasticSearch集群中,节点的状态有Green、Yellow和Red三种
- 1、Green 绿色,表示节点运行状态为健康状态,所有的主分片和副本分片都可以正常工作,集群健康。
- 2、Yellow 黄色,表示节点的运行状态为预警状态,所有的主分片都正常工作,但是至少有一个副分片是不能正常工作的,此时整个的集群是正常工作的,但是在性能方面被弱化了。
- 3、Red 红色,表示整个的集群都无法使用,这个时候表示集群中至少还有一个主分片以及它的所有副分片都不能正常工作,虽然集群查询的操作还可以进行,也只能是返回部分数据。当分配到出问题的地方的时候还会报错,并且还会导致数据丢失。
- 3、Shards 分片,当所有的数据量太大的时候 ,受限于单个节点的内存、磁盘IO等能力,节点无法快速响应客户端的请求,这个时候需要将一个索引上的数据进行水平的拆分,拆分出来的数据每个数据片被称为是一个分片,一般的情况下,每个分片都会放到不同的服务器上。
进行分片操作之后,索引在规模上进行扩大,随之而来的就是性能上的提升。
ElasticSearch依赖的Lucene,ElasticSearch中的每个分片其实都是Lucene中的一个索引文件,所以每个分片必须有一个主分片和多个副本分片。
在实际使用的过程中,开发人员设置有多个分片的索引中写入数据的时候,通过路由来确定到底写入到哪个分片中。所以在创建索引的时候需要设置分片数量,并且分片的数量一旦确定就不能改变。
在查询操作的时候,需要在索引对应的多个分片上进行查询操作,ElasticSearch会把查询发送给每个相关分片,并且汇总各个分片的查询结果,对上层的应用程序而言,分片是透明的,也就是说应用程序并不知道分片的存在。
Elasticsearch中默认为一个索引创建5个分片,并且每个主分片创建一个副本。
- 4、Replicas 备份,也可以称为是副本,副本指的是对主分片的备份,这种备份是精确复制模式。每个主分片有零个或者多个副本,主分片和备份分片都可以对外界提供数据服务,当数据需要进行写入操作的时候,首先在主分片上完成数据索引,然后数据会从主分片上发到备份分片上进行索引。
当主分片不可用的时候,ElasticSearch会在备份分片中选举一个成为主分片,从而避免数据的丢失。
一方面,备份分片既可以提升ElasticSearch系统的高可用性能,又可以提升搜索的时候的并发性能。另一方面,备份分片也是一把双刃剑,如果备份的数量太多,在写操作的时候会增加同步的负担。
- 5、Index 索引,在ElasticSearch中,索引由一个或者多个分片组成。在使用索引的时候,需要通过索引名称在集群内进行唯一标识。
- 6、Type 类别,类别指的是索引内部的逻辑区分,通过Type的名字在索引内进行唯一标识。在查询的时候如果没有该值,则表示需要在整个索引中查询。
- 7、Document,文档,索引中的每一条数据叫做一个文档,与关系型数据库的使用方法类似,一条文档数据通过id 在Type进行唯一标识。
- 8、Settings Settings 是对集群中索引的定义信息,例如一个索引默认的分片数、副本等。
- 9、Mapping Mapping 表示中保存了定义索引中字段Field 的存储类型、分词方式、是否存储等信息,有点类似于关系型数据库中的表结构信息。
在ElasticSearch中,Mapping 是可以动态识别的。如果没有特殊要求,则不需要手动创建Mapping,因为ElasticSearch会根据数据格式自动识别它的类型。当需要对某些字段添加特殊属性的时候,如定义使用其他分词器、是否分词、是否存储等等,就需要手动修改Mapping,一个索引的Mapping一旦创建,已经存储数据了,就不能在修改了。
- 10、Analyer:Analyer 表示的是字段分词方式的定义,一个Analyzer 通常由 一个Tokenizer和零到多个Filter组成,在ElasticSearch中,默认的标准Analyzer 包含一个标准的Tokenizer和三个Filter。Standard Token Filter、Lower Case Token Filter和Stop Token Filter。
ElasticSearch 的架构设计
ElasticSearch架构如下图所示。
自下而上分别是核心层、数据处理层、发现与脚本层、协议层和应用层。
其中,核心层是Lucene框架-ElasticSearch是基于Lucene框架实现。
数据处理层主要是指在ElasticSearch中对数据的加工处理操作,主要有Index、Search、Mapping等模块组成
发现与脚本层主要是Discovery 模块、Script模块和第三方插件模块。Discovery模块是ElasticSearch 自动发现节点的机制。Script模块支持脚本的执行,脚本的应用使得能更方便的的对查询出来的数据进行加工处理。目前ElasticSearch支持JavaScript脚本、Python脚本等。第三方插件模块表示ElasticSearch支持安装更多的第三方插件,例如ElasticSearch-Ik分词插件、ElasticSearch-SQL插件等等。
协议层是ElasticSearch中的数据交互协议,目前支持Thrift、Memcached和HTTP三种协议、默认使用HTTP协议。
应用层是指的是ElasticSearch的API支持模式。ElasticSearch 支持RESTFul风格的API,这种API接口是当下比较流行的API风格。
ElasticSearch的节点自动发现机制
在ElasticSearch 内部,通过在集群中配置一个相同的集群名称,就能将不同的节点进行连接到同一个集群。
ElasticSearch 内部实现了自动发现机制,并且提供了4中可以选择的发现机制。一种是默认实现的,其他的都是通过插件来实现。
- 1、Azure Discovery插件方式:多播模式
- 2、EC2 Discovery插件方式:多播模式
- 3、Google Compute Engine Discovery 插件方式:多播模式
- 4、Zen Discovery 默认实现方式,支持多播模式和单播模式
Zen Discovery 是 ElasticSearch 内置的默认发现模块,发现模块用于发现集群中的节点以及选举主节点。Zen Discovery提供单播模式和基于文件的发现机制,并且可以扩展为通过插件支持其他形式的发现机制。
在配置的时候,需要了解相关的配置参数
- discovery.zen.ping.multicast.enabled 表示关闭多播模式的自动发现机制,主要是为了防止其他机器上的节点自动连入其中。
- discovery.zen.fd.ping_timeout 和 discovery.zen.ping.timeout 表示设置了节点与节点之间连接ping 命令执行的超时时长。
- discovery.zen.minimum_master_nodes 表示集群中选举主节点时至少需要多少个节点接入。
- discovery.zen.ping.unicast_hosts 表示单播模式下,节点应该自动发现那些节点列表。action.auto_create_index:false 表示关闭自动创建索引。
1、单播模式
ElasticSearch 支持单播和多播模式两种节点发现机制,多播模式已经不被大多数的操作系统支持了,再加上其安全性不高,所以一般会主动的关闭多播模式。
在ElasticSearch 中,发现机制默认被配置为使用单播模式,以防止节点无意中加入集群。ElasticSearch支持同一个主机启动多个节点的方式。所以只有在同一台机器上的节点才会自动组成集群。当集群的节点运行在不同的机器上的时候,在单播模式下,需要对ElasticSearch进行配置。
discovery.zen.ping.muticast.enabled:false
discovery.zen.fd.ping_timeout:100s
discovery.zen.ping.timeout:100s
discovery.zen.minimum_master_nodes:2
discovery.zen.ping.unicast.hosts:["192.168.1.2","192.168.1.3","192.168.1.4"]
配置之后,集群构建以及主节点的选举如下操作
节点启动后先执行ping命令,这里并不是系统的ping命令,而是ElasticSearch的RPC操作命令。如果discovery.zen.ping.unicast.hosts有对应的配置,则ping这个配置的列表。
ping命令的返回结果包含该节点的基本信息以及该节点认为的主节点。
在选举开始的时候,主节点先从各节点认为的master中选举,选举规则比较简单,按照ID进行字典排序,第一就是。
如果都没有认为的master节点,则从所有的节点中选择,规则和上面说的一样。
在上面的配置中discovery.zen.minimum_master_nodes 设置了最小的值,表示如果节点数据达不到最小的限制值,那么直到超过该值之后,才会进行选举。
最后,如果选举出来它只有一个本地节点,那么它自己就是主节点。
如果当前节点是主节点,则开始等待节点数达到discovery.zen.minimum_master_nodes配置,在提供服务,如果当前节点不是主节点,则尝试加入主节点所在的集群。
2、多播模式
在多播模式下,需要在每个节点配置好集群的名称与节点即可,相互会通过ElasticSearch自定义的发现协议,按照多播的模式在网络上进行寻找。
节点类型
在ElasticSearch中,每个节点可以有多个角色,节点既可以是候选主节点,也可以是数据节点。
节点的角色配置在配置文件 /config/elasticsearch.yml 进行设置。
node.master:true // 是否为候选主节点
node.data:true //是否为数据节点
数据节点负责数据的存储相关的操作,例如对数据的增删改查聚合等操作。正是因为如此,数据节点往往对服务器的配置要求比较高,特别对CPU、内存和IO的需求很大。除此之外,数据节点梳理通常随着集群的扩展大而弹性增加,以保持ElasticSearch 服务的高性能和高可用。
候选主节点是被选举为主节点的节点,在集群中,只有候选主节点才有选举权和被选举权,其他节点不参与选举工作。
一旦候选节点被选举为主节点,则主节点就要负责创建索引、删除索引、追踪集群中节点的状态,以及跟踪那些节点是集群的一部分,并且决定将分片分配到相关的节点。
分片和路由
在ElasticSearch 中,如果要进行分片和副本的配置,需要提前配置。因为当在一个多分的索引中写入数据的时候,需要通过路由来确定具体写道那个分片中,所以在创建索引的时候需要指定分片的数量,并且分片的数量一旦确定就不能修改。
分片的数量和副本的数量doIU可以通过创建索引的时候Settings来配置,ElasticSearch 默认为一个索引建立5个主分片,并分别为每个分片创建一个副本,参数如下
index.number_of_shards:5
index.number_of_replicas:1
对文档的新建、索引和删除请求等写操作,必须在主分片上完成之后才能被复制到相关的副本分片。ElasticSearch为了加速写入的速度,写入操作往往是并发执行的。为了解决在并发写的过程中出现的数据冲突的问题,ElasticSearch通过乐观锁的方式控制,每个文档都有一个自己的版本号,当文档被修改的时候版本号递增。
分片如何使用?
当向ElasticSearch写入数据的时候,ElasticSearch根据文档标识符ID将文档分配到多个分片上。当查询数据时,ElasticSearch会查询所有的分片并汇总结果。对用户而言,这个过程是透明的,用户并不知道数据到底在哪个分片上。
为了避免在查询的过程中因为部分数据查询结果失败导致影响结果的准确性,ElasticSearch引入了路由的功能,也就是数据在写入的时候,通过路由将数据写入指定的分片,在查询的时候,可以通过相同的路由找到指定的分片查询数据,在默认的情况下,分片算法如下所示
shard_num = hash(_routing) % num_primary_shards
上面 routing 字段的取值默认是id字段或者是parent字段。routing字段在Hash分片之后再与有分片的数量取模,最终确定数据被分配到了那个分片上。
这样做的目的是通过Hash分片来保证每个分片上数据量的均匀分布,避免各个分片上存储的负载不均。在做数据检索的时候,ElasticSearch默认会搜索所有分片上的数据,最后在主节点上汇总各个分片数据进行排序处理之后最终返回结果。
数据写入过程
数据写入操作是在ElasticSearch内存中执行,数据会被分配到特定的分片和副本上,最终需要进行数据持久化操作的。
在ElasticSearch中,数据存储的路径是在配置文件中进行配置的
path.data:/path/to/data //索引数据
path.logs:/path/to/logs // 日志记录
1、分段存储
索引数据在磁盘上是以分段的方式进行存储的。
对于段这个概念,是ElasticSearch从Lucene中继承的概念,在索引中,索引文件被拆分成多个子文件,其中每个文件就叫做段,每个段都是一个倒排索引的小单元。
段具有不变性,一旦索引的数据被写入硬盘,就不能再修改。
为什么要引入分段?
如果所有的文档集合构建在一个很大的倒排索引文件中,且数据量还在不断增加,当进行修改的时候,需要全量更新当前的倒排索引文件,这就会导致整个的数据更新消耗的资源很大。
在Lucene中,分段的存储模式可以避免在写操作的时候使用锁,这样的操作提升了ElasticSearch读写性能。有点类似于CurrentHashMap 中“分段锁” 的概念。都是为了减少锁的使用,提升并发性能。
当分段被写入磁盘后会生成一个提交点,提交点意味着一个用来记录所有段信息的文件已经生成。所以,一个分段有一个提交点。表示此段仅有度的权限,永远失去的写的权限。
当段在内存中时,此时分段拥有只写的权限,数据还是会被不断的写入,而不具备读数据的权限,这就意味着这一部分数据不能被ElasticSearch用户检索到。
既然索引文件分段存储并且不可修改,那么新增、更新和删除是如何完成的?
新增操作是比较容易处理的,既然数据是新数据,那么只需要在当前文档新增一个段就可以了。
删除数据的时候,由于分段不可修改的特性,ElasticSearch不会文档从旧的段中移除,所以就会新增一个文件.del。这个文件会记录被删除文档的信息。被标记删除的文档仍然可以被查询匹配到,但会在最终结果返回之前通过.del文件将其删除。
当数据进行更新的时候,还是由于分段不可修改,ElasticSearch无法通过想修改旧的段来反映文档的更新,由于更新操作是两个操作的结合,也就是需要先删除再新增,ElasticSearch会将旧的文档从.del文件中标记删除,然后将文档的新版本索引到一个新的段中。在查询数据时,两个版本文档都会被一个查询匹配到,但被删除的旧文档的结果会在返回的时候进行移除。
综上,段作为不可修改是具有一定的优势,段的主要优势表现在不需要锁,从而提升了ElasticSearch的性能。
分段不变性的主要的缺点是存储占用空间大,当删除旧数据的时候,旧数据不会被马上删除,而是再.del文件中被标记为删除,而旧数据只能等到更新的时候才会被删除,这样就会导致存储空间的浪费,如果是频繁的更新数据 ,每次更新数据都是新增的数据到新分段中,并且标记旧分段的数据,也会浪费一定的存储空间内。
在删除和更新数据的时候,存储空间的浪费挺多,但是在检索数据的时候,依然是有局限性。在查询到的结果集中会包含所有的结果,所以主节点需要排除被标记删除的旧数据,随之而来的就是查询带来的负担。
2、延迟写策略
在ElasticSearch中,索引写入磁盘的过程是异步的。
所以为了提升写的性能,ElasticSearch并没有每次新增一条数据就加上一个分段在磁盘上,而是采用的延迟策略,延迟写的执行过程如下。
每当有新数据写入的时候,就将其先写入到JVM的内存中。在内存与磁盘之间是文件系统缓存,文件缓存空间使用的是操作系统的空间。当达到默认的时间或者内存中的数据量达到一定数量的时候。会触发一次刷新操作。刷新操作将内存中的数据生成到一个新的分段上并缓存到文件缓存系统中,然后再被刷新到磁盘中并生成提交点。
这里需要说明的一点是,由于新的数据会继续进行写入内存操作,而内存中的数据并不是以段的形式存储,因此不能提供检索功能。只有当数据经过内存刷新之后存入到文件缓存系统,并且生成新段的时候,新的段才能提供搜索功能,而不需要等到被刷新到磁盘之后才可以搜索。
在Elasticsearch 中,写入和打开一个新段的过程叫做刷新,在默认的情况下,每个分片会每秒自动刷新一次。这就是Elasticsearch能做到近时搜索的原因,因为文档的变化并不是立即对搜索可见的,但会在一秒之内变成可见的。
除了自动刷新之外,也可以通过手动的方式刷新。
也可以在建立索引的时候,在Setting中通过配置refresh_interval的值,来调整索引的刷新频率。在设置值的时候需要注意后面需要带上单位,否则默认是毫秒,当refresh_interval=-1的时候,表示关闭了自动刷新的功能。
虽然延迟写策略可以减少数据往磁盘上写的次数,提升Elasticsearch 整体写入能力,但文件缓存系统的引入同时也带来了数据丢失的风险,比如说断电。
为了解决这个问题 Elasticsearch 引入了事务日志 Translog 的机制。事务日志用于记录所有还没有进行持久化到磁盘数据。
在添加了日志机制之后,数据的写入流程变成了下面的样子。
- 1、新文档被搜索之后,先被写入内存中,为了方式数据丢失,Elasticsearch 会追加一份数据到事务日志中。
- 2、新文档持续在被写入内存的时候,同时也会记录到事务日志中。当然,此时的新数据还不能被检索和查询
- 3、当达到默认的刷新事件或者内存中的数据达到一定量之后,Elasticsearch 会触发一次刷新,将内存中的数据以一个新的段的形式刷新到文件缓存系统中并且情况内存,这个时候新的端虽然没有被提交到磁盘中进行持久化存储,但是对于外部的检索操作已经是可以支持的,但是不能被修改。
- 4、随着文档不断地写入操作,当日志数据大小超过某个值的时候,或者超过一段时间之后,这个时候Elasticsearch会触发一次Flush。
这个时候内存中的数据被写入一个新的段,同时被写入文件缓存系统,文件缓存系统中的数据通过Fsync 刷新到磁盘中,生成提交点,而日志文件被删除,创建一个新的日志文件。
3、段合并
在Elasticsearch 自动刷新流程中,每秒都会创建一个新的段,这个自然会导致在短时间内段的数量猛增,当段的数量太多的时候会带来较大的资源消耗,例如对文件句柄、内存和CPU的消耗。在内容搜索阶段,由于搜索请求要检查每个段,然后合并查询结果,所以段越多,搜索的效率越低。
为此,Elasticsearch 引入段合并机制。段合并机制在后台定期执行,从而小的段被合并到大的段,然后这些大的段在合并到更大的段。
在段合并过程中,Elasticsearch 会将那些旧的已删除的文档从文件系统中清除。被删除的文档不会被拷贝到新的大段中,当然,在合并过程中不会中断索引和搜索。
段合并是自动进行索引和搜索的,在合并进程中,会选择一小部分大小相似的段,在后台将它们合并到更大的段中,这些段既可以是未提交的,也可以是已经提交的。
在合并结束后,老的段会被删除,新的段被Flush到磁盘,同时写入一个包含新段且排除旧的和较小的段的新提交点。打开新段之后,可以用来搜索。
由于段合并的计算量较大,对磁盘I/O的消耗也较大,因此段合并会影响正常的数据写入速率。Elasticsearch 并不会让段合并影响搜索性能。Elasticsearch在默认情况下会合并流程进行资源限制,这就使得搜索服务仍然有足够的资源仍然可以执行的原因。
关联知识点
1、乐观锁
Elasticsearch 引入了乐观锁机制来解决并发写过程中数据冲突的问题,乐观锁在多个维度都有应用。
在数据库中,用乐观锁来控制表结构,减少长事务中数据库加锁的开销,达到数据表“读多写少”场景下的高性能;
Java中引入的CAS(Compare And Swap)乐观锁实现机制实现多线程同步的原子指令,例如AtomicInteger 。
2、配置文件格式
配置文件的格式YML。先后经历了ini 格式、JSON格式、XML格式、Properties格式和HOCON格式。
在配置文件的变迁过程中,可以看到配置的方式都在追求语法简单、能继承、支持注释等特性。
3、副本
副本技术是分布式系统中常见的一种数据组织形式,在日常工作中,副本技术也是比较常见的。例如组织人才培养。
在分布式系统中,副本是如何来的,为什么这么与必要性呢?
副本一般指在分布式系统中为数据或者服务提供的冗余。这种冗余设计是提高分布式系统容错率、提高可用性的常用手段。
在服务副本方面,一般指的是在不同服务器中部署同一份代码,例如集群部署,集群中任意一台服务器都是集群中其他服务器的备份或者副本。
在数据副本方面,一般指的是在不同的节点上持久化同一份数据。当某节点中存储的数据丢失的时候,系统就可以通过副本读取到响应的数据。可以说,数据副本是分布式系统解决数据丢失或者异常的唯一手段,所以副本协议成为了整个分布式系统的理论核心。
4、副本的数据一致性
&emps;分布式系统通过副本协议,让用户通过一定的方式即可读取分布式系统内部各个副本的数据,这些数据在一定的约束条件下是相同的,这也就是副本数据的一致性。副本数据一致性是针对分布式系统中各个节点而言的,不是针对某个节点或者某些副本而言。
分布式系统中,一致性分为强一致性、弱一致性、还有介于二者之间的会话一致性和最终一致性。
强一致性是最难实现的,强一致性要求任何时刻用户读取到的数据都是最新的数据,弱一致性与强一致性正好相反,数据更新之后,用户无法一定时间内读取到最新的值,因此在实际中使用的很少。
会话一致性指的是在一次会话内,用户一旦读到某个数据 的某个版本的更新数据,则在这个会话中就不会再读到比当前版本更老旧的数据。最终一致性指的是集群中各个副本的数据最终能达到完全一致性。