栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

ElasticSearch深入

ElasticSearch深入

一、 ElasticSearch 概述
1、定义

(1)官方定义:

        Elasticsearch 是一个分布式的免费开源搜索和分析引擎,适用于包括文本、数字、地理空间、结构化和非结构化数据等在内的所有类型的数据。Elasticsearch 在 Apache Lucene 的基础上开发而成,由 Elasticsearch N.V.(即现在的 Elastic)于 2010 年首次发布。Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件;Elastic Stack 是一套适用于数据采集、扩充、存储、分析和可视化的免费开源工具。人们通常将 Elastic Stack 称为 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana),目前 Elastic Stack 包括一系列丰富的轻量型数据采集代理,这些代理统称为 Beats,可用来向 Elasticsearch 发送数据。详情见 Elasticsearch 是什么? | Elastic

(2)集成定义

       Elaticsearch 简称为 ES ,是一个开源的可扩展的分布式的全文检索引擎,它可以近乎实时的存储、检索数据。本身扩展性很好,可扩展到上百台服务器,处理 PB 级别的数据。ES 使用 Java 开发并使用 Lucene 作为其核心来实现索引和搜索的功能,但是它通过简单的 RestfulAPI 和 Java API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。

2、功能
功能扩展
分布式的搜索引擎分布式:可以自动将海量数据进行分块存储和检索
搜索:百度、谷歌,站内搜索
全文检索模糊查询、相关性排序、高亮登功能
数据分析引擎数据分组聚合(销量排名、访问量排名)
海量数据的处理海量数据处理:分布式
近实时:可以实现秒级数据搜索和分析
3、特点

4、企业使用场景
场景备注案例
搜索电商网站、招聘网站、新闻资讯类网站、各种App内的搜索维基百科、百度百科、Github、StackOverFlow、搜狐新闻、电商商品搜索等
日志分析经典的ELK组合各企业内部搭建的ELK平台
数据预警数据阈值预警反馈电商商品价格报警
数据分析聚类归并进行统计分析销量Top、访问量Top等
商业BI系统基于现有数据进行分析,然后进行预测大型零售超市
  二、 ElasticSearch 核心概念 
概念备注

近实时(Near Realtime)

从写入数据到可以被搜索到有一个小延迟(大概1秒);基于es执行搜索和分析可以达到秒级(倒排索引)
集群(Cluster)

一个集群是由一个或多个节点(服务器)组成的,通过所有的节点一起保存你的全部数据并且提供联合索引和搜索功能的节点集合

节点(Node)

一个节点是一个单一的服务器,是你的集群的一部分,存储数据,并且参与集群的索引和搜索功能

索引(Index)一个索引就是含有某些相似特性的文档的集合

文档(document)

一个文档是一个可被索引的数据的基础单元

映射(Mapping) 

数据如何存放到索引对象上,需要有一个映射配置,包括:数据类型、是否存储、是否分词等。相当于关系型数据库中的表结构
字段(Field)ES的最小单位。一个document里有多个Field,每个Field就是一个数据字段。常用数据类型:ext、keyword、number、array、range、boolean、date、geo_point、ip、 nested、object
分片(Shard)

ES是个分布式的搜索引擎,所以索引通常都会分解成不同部分, 而这些分布在不同节点的数据就是分片。ES自动管理和组织分片, 并在必要的时候对分片数据进行再平衡分配,所以用户基本上不用担心分片的处理细节,一个分片默认最大文档数量是20亿

副本(Replica)ES默认为一个索引创建5个主分片,并分别为其创建一个副本分片。也就是说每个索引都由5个主分片成本,而每个主分片都相应的有一个copy
三、倒排索引原理

1、什么叫倒排索引

       在没有搜索引擎时,我们是直接输入一个网址,然后获取网站内容,这时我们的行为是:document -> to -> words 这种通过文章,获取里面的单词,此谓正向索引(forward index)。

       如果我们希望能够输入一个单词,找到含有这个单词,或者和这个单词有关系的文章:word -> to -> document 通俗的说对于这种通过关键词找文章我们把这种索引,成为倒排索引(inverted index)。

       Elasticsearch 使用一种称为 倒排索引 的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表。

2、倒排索引原理

索引流程 

 以下一个表的记录信息,Elasticsearch分别为每个field都建立了一个倒排索引。

idnamesexage
1Jack18
2Marry18
3Tommy38

Name:

TermPostingList
Jack[1]
Marry[2]
Tommy[3]

Sex:

TermPostingList
[1]
[2,3]

Age:

TermPostingList
18[1,2]
38[3]

Term Index

        B-Tree 通过减少磁盘寻道次数来提高查询性能, Elasticsearch 也是采用同样的思路,直接通过内存查找 term ,不读磁盘,但是如果 term 太多, term dictionary 也会很大,放内存不现实,于是有了 Term Index ,就像字典里的索引页一样,A开头的有哪些 term ,分别在哪页,可以理解term index 是一颗树:

       这棵树不会包含所有的 term ,它包含的是 term 的一些前缀。通过 term index 可以快速地定位到 term dictionary 的某个 offset ,然后从这个位置再往后顺序查找。所以 term index 不需要存下所有的 term ,而仅仅是他们的一些前缀与T erm Dictionary 的 block 之间的映射关系,再结合   FST(Finite State Transducers) 的压缩技术,可以使 term index 缓存到内存中。从 term index 查到对应 term dictionary 的 block 位置之后,再去磁盘上找 term ,大大减少了磁盘随机读的次数。

Term Dictionary

       Elasticsearch 为了能快速找到某个 term ,将所有的term排个序,二分法查找 term , logN 的查找效率,就像通过字典查找一样,这就是 Term Dictionary 。

Posting List

       Elasticsearch 分别为每个field都建立了一个倒排索引,Jack、Mary、18、男这些叫 term ,而 [1,2] 就是 Posting List 。 Posting List 就是一个 int 的数组,存储了所有符合某个 term 的文档 id 。通过 Posting List 这种索引方式似乎可以很快进行查找,比如:要找 age=18 的名字,就直接可以索引到 id 是1,2。

总结

 Elasticsearch的索引思路:

       将磁盘里的东西尽量搬进内存,减少磁盘随机读取次数(同时也利用磁盘顺序读特性),结合各种奇技淫巧的压缩算法,用及其苛刻的态度使用内存。

四、文档增删改和搜索请求
1、query then fetch(es 默认的搜索方式)

       如果你搜索时, 没有指定搜索方式, 就是使用的这种搜索方式。 这种搜索方式, 大概分两个步骤:
        1、先向所有的 shard 发出请求, 各分片只返回文档 id(注意, 不包括文档 document)和排名相关的信息(也就是文档对应的分值), 然后按照各分片返回的文档的分数进行重新排序和排名, 取前 size 个文档。

        2、根据文档 id 去相关的 shard 取 document。 这种方式返回的 document 数量与用户要 求的大小是相等的。

        详细过程:

1.发送查询到每个shard
2.找到所有匹配的文档,并使用本地的Term/document Frequency信息进行打分
3.对结果构建一个优先队列(排序,标页等) 
4.返回关于结果的元数据到请求节点。注意,实际文档还没有发送,只是分数 
5.来自所有shard的分数合并起来,并在请求节点上进行排序,文档被按照查询要求进行选择 
6.最终,实际文档从他们各自所在的独立的shard上检索出来
7.结果被返回给用户

2、增删改流程

(1)客户端首先会选择一个节点node发送请求过去,这个节点node可能是协调节点coordinating node
(2)协调节点coordinating node会对document数据进行路由,将请求转发给对应的node(含有 primary shard)
(3)实际上node的primary shard会处理请求,然后将数据同步到对应的含有replica shard的node 
(4)协调节点coordinating node如果发现含有primary shard的node和所有的含有replica shard的 node符合要求的数量之后,就会返回响应结果给客户端。

3、Search流程

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

五、索引文档写入和近实时搜索原理
1、基本概念

Segments in Lucene

       众所周知,Elasticsearch 存储的基本单元是 shard , ES 中一个 Index 可能分为多个 shard, 事实上每个 shard 都是一个 Lucence 的 Index,并且每个 Lucence Index 由多个 Segment 组成, 每个 Segment 事实上是一些倒排索引的集合, 每次创建一个新的 document , 都会归属于一个新的 Segment, 而不会去修改原来的 Segment 。且每次的文档删除操作,会仅仅标记 Segment 中该文档为删除状态, 而不会真正的立马物理删除, 所以说 ES 的 index 可以理解为一个抽象的概念。

Commits in Lucene

       Commit 操作意味着将 Segment 合并,并写入磁盘。保证内存数据尽量不丢。但刷盘是很重的 IO 操 作, 所以为了机器性能和近实时搜索, 并不会刷盘那么及时。 

Translog

       新文档被索引意味着文档会被首先写入内存 buffer 和 translog 文件。每个 shard 都对应一个 translog 文件。

Refresh in Elasticsearch

        在 Elasticsearch 中, 操作默认每秒执行一次, 意味着将内存 buffer 的数据写入到一个新 的 Segment 中,这个时候索引变成了可被检索的。写入新Segment后 会清空内存buffer。

Flush in Elasticsearch

Flush 操作意味着将内存 buffer 的数据全都写入新的 Segments 中, 并将内存中所有的 Segments 全部刷盘, 并且清空 translog 日志的过程。

 2、近实时搜索

       提交(Commiting)一个新的段到磁盘需要一个 fsync 来确保段被物理性地写入磁盘,这样在断电的时候就不会丢失数据。但是 fsync 操作代价很大;如果每次索引一个文档都去执行一次的话会造成很大的性能问题。

       我们需要的是一个更轻量的方式来使一个文档可被搜索,这意味着 fsync 要从整个过程中被移除。

       在 Elasticsearch 和磁盘之间是文件系统缓存。 像之前描述的一样,在内存索引缓冲区中的文档会被写入到一个新的段中。 但是这里新段会被先写入到文件系统缓存,这一步代价会比较低,稍后再被刷新到磁盘,这一步代价比较高。不过只要文件已经在系统缓存中, 就可以像其它文件一样被打开和读取了。

在内存缓冲区中包含了新文档的 Lucene 索引

       Lucene 允许新段被写入和打开--使其包含的文档在未进行一次完整提交时便对搜索可见。 这种方式比进行一次提交代价要小得多,并且在不影响性能的前提下可以被频繁地执行。

 缓冲区的内容已经被写入一个可被搜索的段中,但还没有进行提交

原理 

       下图表示是 es 写操作流程,当一个写请求发送到 es 后,es 将数据写入 memory buffer 中,并添加事 务日志( translog )。如果每次一条数据写入内存后立即写到硬盘文件上,由于写入的数据肯定是离 散的,因此写入硬盘的操作也就是随机写入了。硬盘随机写入的效率相当低,会严重降低es的性能。

        因此 es 在设计时在 和硬盘间加入了 Linux 的高速缓存( )来提 高 es 的写效率。

        当写请求发送到 es 后,es 将数据暂时写入 memory buffer 中,此时写入的数据还不能被查询到。默 认设置下,es 每1秒钟将 memory buffer 中的数据 refresh 到 Linux 的 File system cache ,并清 空 memory buffer ,此时写入的数据就可以被查询到了。

 Refresh API

       在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫做 refresh 。 默认情况下每个分片会每秒自动刷新一次。这就是为什么我们说 Elasticsearch 是 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见。

       这些行为可能会对新用户造成困惑:他们索引了一个文档然后尝试搜索它,但却没有搜到。这个问题的解决办法是用 refresh API 执行一次手动刷新:

1.POST  /_refresh
2.POST  /my_blogs/_refresh
3.PUT   /my_blogs/_doc/1?refresh
{"test": "test"}
PUT /test/_doc/2?refresh=true
{"test": "test"}

1. 刷新(Refresh)所有的索引。

2. 只刷新(Refresh) blogs 索引

3. 只刷新 文档

      并不是所有的情况都需要每秒刷新。可能你正在使用 Elasticsearch 索引大量的日志文件, 你可能想优化索引速度而不是近实时搜索,可以通过设置 refresh_interval , 降低每个索引的刷新频率。

PUT /my_logs
{
  "settings":  {  "refresh_interval": "30s" }
}

refresh_interval 可以在既存索引上进行动态更新。 在生产环境中,当你正在建立一个大的新索引 时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来:

PUT /my_logs/_settings
{  "refresh_interval":  -1  }
PUT /my_logs/_settings
{  "refresh_interval":  "1s"  }

 3、持久化变更

原理

如果没有用 fsync 把数据从文件系统缓存刷(flush)到硬盘,我们不能保证数据在断电甚至是程序正 常退出之后依然存在。为了保证 Elasticsearch 的可靠性,需要确保数据变化被持久化到磁盘。

在动态更新索引时,我们说一次完整的提交会将段刷到磁盘,并写入一个包含所有段列表的提交点。 Elasticsearch 在启动或重新打开一个索引的过程中使用这个提交点来判断哪些段隶属于当前分片。

即使通过每秒刷新(refresh)实现了近实时搜索,我们仍然需要经常进行完整提交来确保能从失败中 恢复。但在两次提交之间发生变化的文档怎么办?我们也不希望丢失掉这些数据。

Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了 日志记录。通过 translog ,整个流程看起来是下面这样:

1. 一个文档被索引之后,就会被添加到内存缓冲区, 追加到了 translog ,正如 图21描述的一 样。

新的文档被添加到内存缓冲区并且被追加到了事务日志

 

1. 刷新(refresh)使分片处于 图 22描述的状态,分片每秒被刷新(refresh)一次:

这些在内存缓冲区的文档被写入到一个新的段中,且没有进行 fsync 操作。 这个段被打开,使其可被搜索。
内存缓冲区被清空。

刷新(refresh)完成后, 缓存被清空但是事务日志不会

2. 这个进程继续工作,更多的文档被添加到内存缓冲区和追加到事务日志(见 图 23 )。 图23事务日志不断积累文档 

3. 每隔一段时间--例如 translog 变得越来越大--索引被刷新(flush);一个新的 translog 被创建, 并且一个全量提交被执行(见 图 24):

所有在内存缓冲区的文档都被写入一个新的段。 缓冲区被清空。
一个提交点被写入硬盘。
文件系统缓存通过 fsync 被刷新(flush)。 老的 translog 被删除。

translog 提供所有还没有被刷到磁盘的操作的一个持久化纪录。当 Elasticsearch 启动的时候, 它会从 磁盘中使用最后一个提交点去恢复已知的段,并且会重放 translog 中所有在最后一次提交后发生的变 更操作。

translog 也被用来提供实时 CRUD 。当你试着通过 ID 查询、更新、删除一个文档,它会在尝试从相应 的段中检索之前, 首先检查 translog 任何最近的变更。这意味着它总是能够实时地获取到文档的最新 版本。

在刷新(flush)之后,段被全量提交,并且事务日志被清空

 

flush API

这个执行一个提交并且截断 translog 的行为在 Elasticsearch 被称作一次 flush 。 分片每 30 分钟被自 动刷新(flush),或者在 translog 太大的时候也会刷新。

flush API 可以 被用来执行一个手工的刷新(flush):

 

POST /blogs/_flush
POST /_flush?wait_for_ongoin

1. 刷新(flush) blogs 索引。
2. 刷新(flush)所有的索引并且等待所有刷新在返回前完成。

我们很少需要自己手动执行一个的 flush 操作;通常情况下,自动刷新就足够了。 这就是说,在重启节点或关闭索引之前执行 flush有益于你的索引。当 Elasticsearch 尝试恢复或重新打

开一个索引, 它需要重放 translog 中所有的操作,所以如果日志越短,恢复越快。

Translog 有多安全?
translog 的目的是保证操作不会丢失。这引出了这个问题: Translog 有多安全?
在文件被 fsync 到磁盘前,被写入的文件在重启之后就会丢失。默认 translog 是每 5 秒被

fsync 刷新到硬盘, 或者在每次写请求完成之后执行(e.g. index, delete, update, bulk)。这个过 程在主分片和复制分片都会发生。最终, 基本上,这意味着在整个请求被 fsync 到主分片和复 制分片的 translog 之前,你的客户端不会得到一个 200 OK 响应。
在每次写请求后都执行一个 fsync 会带来一些性能损失,尽管实践表明这种损失相对较小(特别 是 bulk 导入,它在一次请求中平摊了大量文档的开销)。

但是对于一些大容量的偶尔丢失几秒数据问题也并不严重的集群,使用异步的 fsync 还是比较有 益的。比如,写入的数据被缓存到内存中,再每 5 秒执行一次 fsync 。

这个行为可以通过设置 durability 参数为 async 来启用:

PUT /my_index/_settings {
    "index.translog.durability":  "async",
    "index.translog.sync_interval":  "5s"
}

这个选项可以针对索引单独设置,并且可以动态进行修改。如果你决定使用异步 translog 的话,你需 要 在发生 crash 时,丢失掉 时间段的数据也无所谓。请在决定前知晓这个特 性。

如果你不确定这个行为的后果,最好是使用默认的参数(

)来避免数据丢失。

六、索引文档存储段合并机制 段合并机制

       由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较 大的麻烦。 每一个段都会消耗文件句柄、内存和 CPU 运行周期。更重要的是,每个搜索请求都必须轮 流检查每个段;所以段越多,搜索也就越慢。

Elasticsearch 通过在后台进行段合并来解决这个问题。小的段被合并到大的段,然后这些大的段再被合 并到更大的段。段合并的时候会将那些旧的已删除文档 从文件系统中清除。 被删除的文档(或被更新 文档的旧版本)不会被拷贝到新的大段中。

启动段合并在进行索引和搜索时会自动进行。这个流程:

1、 当索引的时候,刷新(refresh)操作会创建新的段并将段打开以供搜索使用。

2、 合并进程选择一小部分大小相似的段,并且在后台将它们合并到更大的段中。这并不会中断索引和 搜索。

两个提交了的段和一个未提交的段正在被合并到一个更大的段

3、合并完成时的活动:

新的段被刷新(flush)到了磁盘。 写入一个包含新段且排除旧的和较小的段的新提交点。、

新的段被打开用来搜索。

老的段被删除。

一旦合并结束,老的段被删除

合并大的段需要消耗大量的 I/O 和 CPU 资源,如果任其发展会影响搜索性能。Elasticsearch 在默认情 况下会对合并流程进行资源限制,所以搜索仍然 有足够的资源很好地执行。默认情况下,归并线程的限 速配置 indices.store.throttle.max_bytes_per_sec 是 20MB。对于写入量较大,磁盘转速较高,甚至 使用 SSD 盘的服务器来说,这个限速是明显过低的。对于 ELK Stack 应用,建议可以适当调大到 100MB或者更高。

PUT /_cluster/settings
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
} }

 

用于控制归并线程的数目,推荐设置为cpu核心数的一半。 如果觉得自己磁盘性能跟不上,可以降低配 置,免得IO情况瓶颈。

index.merge.scheduler.max_thread_count

归并策略 policy、

归并线程是按照一定的运行策略来挑选 segment 进行归并的。主要有以下几条: index.merge.policy.floor_segment 默认 2MB,小于这个大小的 segment,优先被归并。 index.merge.policy.max_merge_at_once 默认一次最多归并 10 个 segment index.merge.policy.max_merge_at_once_explicit 默认 optimize 时一次最多归并 30 个 segment。
index.merge.policy.max_merged_segment 默认 5 GB,大于这个大小的 segment,不用参与归 并。optimize 除外。

optimize API

optimize API 大可看做是 API。它会将一个分片强制合并到 max_num_segments 参数指定 大小的段数目。 这样做的意图是减少段的数量(通常减少到一个),来提升搜索性能。 在特定情况下,使用 optimize API 颇有益处。例如在日志这种用例下,每天、每周、每月的日志被存 储在一个索引中。 老的索引实质上是只读的;它们也并不太可能会发生变化。在这种情况下,使用

并合制强

optimize 优化老的索引,将每一个分片合并为一个单独的段就很有用了;这样既可以节省资源,也可以 使搜索更加快速:

POST /logstash-2014-10/_optimize?max_num_segments=1

forceMergeRequest.maxNumSegments(1)

 

感谢大家,有不同的观点交流,稳步提升!!!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/582433.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号