ES相关概念与工作原理
# ES相关概念
# Lucene
- Lucene是至今公认的最好用的搜索引擎库,ES就是基于Lucene进行开发的。
- ES中处理分词、构建倒排索引等等工作都Lucene做的,实际上Lucene只是一个提供全文搜索功能类库的核心工具包,为ES框架提供一个搜索引擎基础。
# 集群(cluster)
- 由一个或多个节点组成,并通过集群名称与其他集群进行区分,ES集群是有中心集群。
# 节点(node)
- 一个节点就是一个ElasticSearch实例。
- 在ES中存在两种节点:主节点和副本节点。
- 主节点作为权威节点,负责索引的创建和删除、统计集群和节点的状态信息、索引分配的管理、管理节点等,稳定的主节点对集群的健康是非常重要的。
- 副本节点会同步主节点的数据,并在主节点宕机时升级为新的主节点以保证服务运行。
- 主节点和副本节点切换的方式类似于KeepAlived主备切换的实现方式。
# 索引(index)
- 在ES中索引是一个拥有相似特征的文档的集合,一个集群中可以有任意多个索引,索引的数据会被分配到各个分片上。
- 它类似于MySQL中的表,比如说订单日志表、访问日志表等。
# 文档(document)
- 文档是一个可被索引的基础信息单元,也就是一条数据,文档以JSON格式来表示。
- 它类似于MySQL中的记录行。
# 字段(field)
- 字段用于对文档中不同含义的数据进行分类。
- 类似于MySQL中表的字段,比如name、age、gender等字段。
# 映射(mapping)
- mapping用于对文档数据做出一些规则方面的限制。比如某个字段的数据类型、默认值、分析器、是否能被索引等,都是由映射来设置的。
- 类似于MYSQL中的字段约束,比如MYSQL中字段的数据类型、是否不为空等。
# 分片(shard)
- 分片是ES中的最小工作单元,因为ES是个分布式的搜索引擎,所以ES中会将一个Index分为多个分片,且这些分片可以分布在不同的节点之上。
- 一个分片便是一个Lucene的实例,它本身就是一个功能完整的搜索引擎,只不过每个分片只有Index的部分数据。我们的文档和逆向索引会被存储到分片内。
- 在我们创建索引时,可以指定想要的分片数量,一个分片默认最大文档数量是20亿,ES能够自动管理和组织分片,并在必要的时候对分片数据进行再平衡分配,所以用户基本上不用担心分片的处理细节。
- 另外分片还提供基本的负载均衡功能,比如现在有10个分片,集群中有3个节点,ES会均衡的进行分配,以保证每个节点均衡的负载请求。
# ES副本分片
# 介绍
- 副本分片是主分片的冗余副本,主要目的就是提供故障转移功能,来提升ES集群的高可用性,如果主分片所在的ES节点挂了,在另一个ES节点上的副本分片就会晋升为主分片,来取代挂掉的主分片。
- 在索引写入时,副本分片做着与主分片相同的工作。新文档被写入主分片的索引后,会同步到其它所有的副本分片。增加副本数并不会增加索引容量,但是由于副本分片也会用于处理查询请求,所以我们也可以通过增加副本来利用更多的硬件性能,提升查询能力。
# 副本分片数量的作用
- 在生产环境中,随着数据集的增长,不合理的分配策略可能会给系统的扩展带来严重的问题。
- 对于分布式搜索引擎来说,分片及副本的分配将是高可用及快速搜索响应的设计核心。
- 主分片与副本都能处理查询请求,它们的唯一区别在于只有主分片才能处理索引请求。
- 副本对搜索性能非常重要,同时用户也可在任何时候添加或删除副本。额外的副本能给你带来更大的容量, 更高的呑吐能力及更强的故障恢复能力。
# 谨慎分片
- 在ElasticSearch集群中配置好索引后, 在集群运行中就无法调整分片设置。既便以后需要调整分片数量,也只能新建创建并对数据进行重新索引。
- 每分配一个分片,都会有额外的成本,每个分片本质上就是一个Lucene索引,因此会消耗相应的文件句柄,内存和CPU资源。
- 每个查询请求会调度到索引的每个分片中。如果分片分散在不同的节点倒是问题不太。但当分片开始竞争相同的硬件资源时,性能便会逐步下降。
- ES使用词频统计来计算相关性。当然这些统计也会分配到各个分片上。如果在大量分片上只维护了很少的数据,则将导致最终的文档相关性较差。
# 分片创建原则
- 索引分片个数创建原则:1.5~3倍原则创建分片, 例如:如果你有2个节点,则推荐创建的分片数最多不超过6(2x3)个。
- 限制节点中分片最大容量:ES官方推荐的最大JVM堆空间是30~32G,所以我们的分片最大容量也应当限制为30GB。
- 限制节点中分片最大数量:且还建议节点最大分片数量最好按照JVM内存来进行计算,每1Gb内存可以为20个分片,假设我们的JVM设置为60G,那么节点上分片数量最多应当不超过1200个。
# ES与MYSQL概念对比
- ES中的索引对应MYSQL中的库。
- ES中的文档对应MYSQL中的记录行。
- ES的字段对应MYSQL中的列。
# ES端口
# 9200端口
- 9200端口是ES中HTTP协议的RESTful端口,我们调用ES API就是使用该端口。
# 9300端口
- 9300端口为ES集群中各组件通信使用的端口。
# ES工作原理相关概念
# 逆向索引(inverted index)
# 逆向索引介绍
逆向索引(InvertedIndex)又称反向索引、倒排索引,它是⼀种索引结构,它存储了单词与单词⾃⾝在⼀个或多个⽂档中所在位置之间的映射,这些的单词称为Term。
我们在搜索关键字时,所有包含关键字的文档都会被匹配,该特性也叫做全文搜索。逆向索引能够以文档内容中的各个词作为关键字,匹配出有该关键字存在的所有文档,然后以此来获取有该关键字的文档内容,强调关键字和文档的关联。
ES中会维护一个逆向索引表,表内包含所有文档中出现过的所有单词,同时记录了这个单词在哪个文档中出现过,也就是说所有数据都是索引。
# 例如 文档:1001.txt 内容:my zhang 文档:1002.txt 内容:my niu # 到逆向索引表后就是 keyword documents ----------------------- my 1001.txt, 1002.txt zhang 1001.txt niu 1002.txt
1
2
3
4
5
6
7
8
9
10- 查询关键字时,会匹配关键字存在的所有文档,并根据词频按顺序展示出来。
而正向索引则是通过特定的索引关键字,查询关键字对应的其他记录行,强调关键字和它所在的行的关联,正向索引如果通过行记录内容中的单词进行模糊查询则会很慢,所以在这方面它不如逆向索引。
MySQL是默认不加索引,要加索引必须特别说明。而ES则是所有数据默认进行索引的,只有不加索引才需要说明。
# 逆向索引构成
- 逆向索引里面不止记录了单词与文档的对应关系,它还维护了很多其他有用的数据。比如文档一共包含了多少个单词、文档的长度、所有文档的总长度等。
- 这些数据用来给搜索结果进行打分,比如搜索"tb"时,那么出现"tb"这个单词频率最多的文档会被优先返回,因为它匹配的次数最多,和我们的搜索条件关联性最大,因此得分也最多。
# 逆向索引特点
- 逆向索引是不可更改的,一旦它被建立了,里面的数据就不会再进行更改。
- 这种特性的优点
- 没有必要给逆向索引查询加锁,因为不允许被更改,只有读操作,所以就不用考虑多线程导致互斥等问题。
- 索引一旦被加载到了缓存中,大部分访问操作都是对内存的读操作,省去了访问磁盘带来的IO开销。
- 因为逆向索引的不可变性,所有基于该索引而产生的缓存也不需要更改,因为没有数据变更。
- 使用逆向索引还可以压缩数据,减少磁盘IO及对内存的消耗。
# 段文件(Segment file)
- Segment file实际就是存储逆向索引的文件,ES中的Index实际就是由多个segment组成的,每个segment事实上就是一些逆向索引的集合。也就是说一个Index中包含多个segment,一个segment包含多个文档。
- 我们在创建一个Document时,会先将Document数据保存在Buffer中,然后每隔1秒执行refresh操作将Buffer中的数据写入到文件系统缓存中的新segment文件中,写入之后就会清空Buffer,并在之后执行flush操作时实际写入到磁盘。
- 我们在删除一个Document时,并不会真的立马进行物理删除,而是先在Segment中标记该文档为删除状态。
# 提交(commit)
- commit实际就是将缓冲中的数据刷写到磁盘中segment文件的过程。
- 我们新增的文档首先会被存放在内存缓冲区中,当文档数足够多或者到达一定时间时,就会对缓冲进行flush操作进行提交,然后写入到磁盘中。
- ES中还有一种轻量级的提交,也就是refresh操作,该操作是每隔1秒就将内存缓冲区的数据写入到文件系统缓存而非磁盘中,以快速提供文档查询的实时性,同时减少磁盘IO。
# 提交点(commit point)
- 提交点(commit point)也是一个文件,它用于记录当前所有可用的segment。然而由于segment是不可变的,所以每个commit point都会维护一个
.del
文件,.del
文件用于记录执行了删除操作的文档与对应segment的信息。 - 被标记为删除的文档是无法通过请求查询到的。当ES做文档删改操作时首先会在.del文件中声明该Document已经被删除,之后在查询请求过来时,即便segment中仍然可以查询到被标记为删除的文档,在返回查询结果的时候,ES也会根据对应commit point所维护的对应
.del
文件中的信息,将已被标记为删除的文档进行过滤。
# 合并(merge)
- 我们每隔1秒就会生成一个segment文件,最终会导致segment文件非常多,在我们进行查询时就需要依次扫描所有segment文件,查询效率变慢。
- 所以ES会定期自动进行segment merge合并操作,将多个segment文件合并然后写入到一个新segment文件中,然后删除旧的segment文件,另外在合并过程中会将已经标注删除的文档进行真正的删除。
- 对文件系统中零散的小segment进行合并,合并为一个大的segment,来减少search期间依次扫描多个segment带来的资源消耗。
# refresh
- refresh是较为轻量的提交操作,并非完整的提交,因为它只是将内存中的文档数据写入到文件系统缓存区中,并没有写入数据到磁盘。
- 当我们创建一个新文档时,首先会将其写入到内存缓冲中,然后默认每隔1秒会从内存缓冲中将数据写入到文件系统缓存区中的一个segment中,写完之后会清空数据缓冲区,同时索引会变成可被搜索,这个操作叫做refresh。另外它不会清空translog日志,因为数据并没有被写入磁盘。
- 之后查询新写入的文档,即便数据还没有被实际写入到磁盘也可以查询,因为在文件系统缓存区,能够直接从文件系统缓存区获取数据。
- 它使得ES能够提供近实时的搜索,近实时是因为从写入到能够查询需要经过1秒,所以叫近实时。且refresh资源消耗相对较小,能够有效减少磁盘IO导致的资源消耗。
# fsync
- fsync是一个系统调用函数,用于将文件系统缓存中的所有segment刷写到磁盘的操作。
# flush
- flush是完整的提交操作,它会将内存缓冲中的文档数据刷写到磁盘中一个新的segment文件中。
- 首先ES会强制执行一次refresh操作,将内存缓冲中的数据写到文件系统缓存中,然后执行fsync操作,将文件系统缓存中的数据刷到磁盘,同时还会将所有segment信息写入一个新的commit point文件。最后会清空内存缓冲区和translog日志,然后缓冲新的数据并记录新的日志。
- 在手动调用flush api、translog日志文件大小超过设定阈值、merge操作后、ES每隔30分钟定时触发时,会执行flush操作。
# translog日志
介绍
- translog就是ES的一个事务日志,每个分片都对应有一个translog日志文件。该日志主要用于保证可靠存储、数据不丢失,防止ES节点宕机或者其他故障时可能造成的数据丢失。
记录日志
- 在文档发生写操作或者修改操作时,文档不仅会写入到内存缓存区,还会记录到事务日志中,translog日志用于保证还没有被刷到磁盘的文档操作的持久化。
故障恢复
- 在发生的断电或者其它故障时,ES中缓冲区的文档数据可能由于故障原因并没有从内存写到磁盘上,此时就需要通过translog日志进行回放,ES重新启动时,会找到磁盘中的最后一个提交点,然后将该提交点之后的发生的写操作或者修改操作通过translog进行重放,重构内存中的segment。
日志清理
- translog会在ES执行flush操作之后进行日志清理,因为在执行flush操作之后,ES会将文档数据会从缓冲区刷写到磁盘了,此时数据已经持久化,translog中的日志就会失去价值,所以应当被清理掉,然后继续记录未被刷写到磁盘的数据。
相关参数
index.translog.durability: request
- 控制translog日志的写入模式request
- 同步写入(默认),每次写文档或者修改文档就进行日志写入磁盘的操作。async
- 异步写入,按照指定间隔时间执行日志写入磁盘的操作,如果应用场景对数据安全性要求不大,可以使用异步模式提高写入效率。
index.translog.sync_interval: 5
- 指定异步模式下的日志写入间隔时间- 默认是5秒,也就是说如果开启了异步模式,则在这5s内发生断电,数据也是会丢失的。
index.translog.flush_threshold_size: 512mb
- translog日志大小阈值- 默认是512MB,如果translog日志文件大小超过了设定的阈值,就会立刻执行flush操作,将缓冲中的文档数据写入磁盘中,然后清空日志文件。
# ES数据写入过程
- 用户向一个节点提交了一个创建文档的请求。
- 主节点计算新文档应该加入到哪个分片(shard)中,然后根据分片对应的节点信息,将创建文档请求发送给对应的节点。
- 对应节点的ES实例收到请求后,会先写入内存buffer和磁盘上的translog日志文件中。在保证操作写入translog日志后,才会返回操作结果给客户端。
- ES默认每隔1秒或者在buffer快满时执行一次refresh操作,将内存buffer中的数据写入文件系统缓存中的segment,然后清空内存buffer,此时文档将变成可被搜索。
- 不断有新的文档写入,则以上过程将不断重复执行。每隔1秒在文件系统缓存中生成一个新的segment,当translog日志文件大小超过设定阈值后,就会触发flush操作。
- flush操作会先执行refresh操作,然后再执行fsync操作将文件系统缓存中的数据写入到磁盘中,然后触发清空内存buffer和translog日志。