一、 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)

单台服务器,节点存储数据并参与集群的索引和搜索。

主节点(Master Node ):控制Elasticsearch集群,并负责所有集群范围的操作,如添加/删除节点。
数据节点(Data Node ):存储数据并执行与数据相关的操作,例如搜索和聚合。
客户端/路由节点(Client Node ):将集群请求转发到主节点,将与数据相关的请求转发到数据节点。

分片(Shard)

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

副本(Replica) 分片可以制作一个或多个副本,用于防止硬件故障和提高查询能力。ES默认为一个索引创建5个主分片,并分别为其创建一个副本分片。也就是说每个索引都由5个主分片成本,而每个主分片都相应的有一个copy。
索引(Index)     一个索引就是含有某些相似特性的文档的集合。

文档(Document)

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

映射(Mapping) 

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

三、倒排索引原理


1、什么叫倒排索引

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

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

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

2、倒排索引原理

索引流程

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

id name sex age
1 Jack 18
2 Marry 18
3 Tommy 38

Name:

Term PostingList
Jack [1]
Marry [2]
Tommy [3]

Sex:

Term PostingList
[1]
[2,3]

Age:

Term PostingList
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。

四、索引写入和检索请求


1、索引写入流程

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

2、Search流程(query then fetch)

(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 的高速缓存(File System Cache)来提 高 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 ,如下图所示:

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

(2)刷新(Refresh)使分片处于下图描述的状态,分片每秒被刷新(Refresh)一次:

这些在内存缓冲区的文档被写入到一个新的段中,且没有进行 fsync 操作。 这个段被打开,使其可被搜索。内存缓冲区被清空。刷新(Refresh)完成后, 缓存被清空但是事务日志不会。

(3)这个进程继续工作,更多的文档被添加到内存缓冲区和追加到事务日志(见下图)。

(4)每隔一段时间,例如 translog 变得越来越大,索引被刷新(Flush),内存中的 translog 文件被清空。一个新的 translog 被创建, 并且一个全量提交被执行(见下图)。

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

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

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

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

Flush API

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

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

POST /_flush?wait_for_ongoin
POST /blogs/_flush

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

我们很少需要自己手动执行一个的 Flush 操作。通常情况下,自动刷新就足够了。这就是说,在重启节点或关闭索引之前执行 Flush 有益于你的索引。当 Elasticsearch 尝试恢复或重新打开一个索引, 它需要重放 translog 中所有的操作,所以如果日志越短,恢复越快。

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


1、段合并机制

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

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

启动段合并在进行索引和搜索时会自动进行。具体流程如下:
        (1)当索引的时候,刷新(Refresh)操作会创建新的段并将段打开以供搜索使用。
        (2)合并进程选择一小部分大小相似(默认75%,可配置)的段,并且在后台将它们合并到更大的段中。这并不会中断索引和搜索。

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

(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 核心数的一半。 如果觉得自己磁盘性能跟不上,可以降低配置,避免发生 I/O 瓶颈。可使用 index.merge.scheduler.max_thread_count 参数来进行配置。

2、归并策略 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 除外。

3、Optimize API

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

POST /logstash-2014-10/_optimize?max_num_segments=1
forceMergeRequest.maxNumSegments(1)

总结

平时也在使用 ElasticSearch 做开发,对 ES 的一些整理和认识,分享给大家。希望大家多多支持,有问题请及时跟我沟通交流,感谢大家!!!

参考资料

ES(Elasticsearch)核心概念 - 简书

ElasticSearch核心概念和原理 - yang-leo - 博客园

lucene字典实现原理 - zhanlijun - 博客园

ES写入过程_day_ue的博客-CSDN博客

ElasticSearch深入浅出相关推荐

  1. 手撸架构,Elasticsearch 面试25问

    技术栈 传送门 JAVA 基础 手撸架构,Java基础面试100问_vincent-CSDN博客 JAVA 集合 手撸架构,JAVA集合面试60问_vincent-CSDN博客 JVM 虚拟机 手撸架 ...

  2. spring 2.0核心技术与最佳实践 pdf_推荐 Spring Boot 实践学习案例大全 数据缓存 和中间件 安全权限...

    概况 spring boot 实践学习案例 spring boot 初学者及核心技术巩固的最佳实践 目录 『 Spring Boot 2 快速教程 』 Spring Boot 2:WebFlux集成 ...

  3. 安排,Elasticsearch Stack深入浅出视频教程

    来源: 来自网络,如侵权请告知博主删除????. 仅学习使用,请勿用于其他- 最近有小伙伴管我要 Elasticsearch 的视频,给大家找了一个视频,基本操作都很全面,如果想学习的话,可以花周六日 ...

  4. 终于有人把 Elasticsearch 原理讲透了

    上个世纪末,我在广州做 PHP 程序员,那会儿程序员的门槛真低啊.我的上司是技术经理,非常照顾我.有个客户是开律所的,过来提了一个私活的需求,要做个法律查询的网站. 上司让我赚点外块,说干就干,整个 ...

  5. 数据库、缓存、消息队列之外的下一站——Elasticsearch

    搜索是现代软件必备的一项基础功能,而 Elasticsearch 就是一款功能强大的开源分布式搜索与数据分析引擎. 它可以从海量数据中快速找到相关信息,在同领域内几乎没有竞争对手--近两年 DBRan ...

  6. 使用Elasticsearch 构建 .NET 企业级搜索

    最近几年出现的云计算为组织和用户带来了福音.组织对客户的了解达到前所未有的透彻,并能够采用个性化通信锁定客户.用户几乎可以随时随地获取其数据,使其更加易于访问和使用.为了存储所有这些数据,大型数据中心 ...

  7. Elasticsearch 的使用,看这一篇就够了!

    前两天看到曹政大佬的文章,说他上个世纪末在广州做 PHP 程序员的时候,接到一个私活儿. 有个客户是开律所的,要做个法律查询的网站.他整了个 Mysql,把内容导进去,写个搜索查询语句就搞定了.客户觉 ...

  8. 【Elasticsearch】基于儿童积木玩具图解 Elasticsearch 聚合

    1.概述 转载:https://elastic.blog.csdn.net/article/details/113011288 故事得从这一筐积木说起- 周末带孩子正准备玩积木的时候,手机响了,死磕 ...

  9. elasticsearch最大节点数_Elasticsearch究竟要设置多少分片数?

    0.引言 本文翻译自Elasticsearch20170918热乎的官方博客,原作者:Christian Dahlqvist. 在构建Elasticsearch集群的初期如果集群分片设置不合理,可能在 ...

最新文章

  1. linux mysql 升级_linux升级mysql
  2. PHP安装编译错误及解决办法
  3. Android开发入门教程--Android应用程序结构分析
  4. 将数据传入重定向网页
  5. DB2更改数据文件路径
  6. 用Python开始机器学习(4:KNN分类算法)
  7. python学习实例(5)
  8. 优秀的CSS布局大全
  9. IOS一些常用的越狱渠道
  10. iOS 的TextView的常规用法
  11. matlab fseek ftell,fseek函数、ftell函数和fflush函数
  12. HeadFirstJava 10数字与静态
  13. Keil 中 Error L6002U
  14. passenger多ruby版本共存部署
  15. react 返回一个页面_react-navigation goBack返回指定页面
  16. 中古消费热潮下,爆爆奢漫步二奢直播新时代
  17. 毕业季--写给大学毕业生的一番话
  18. 转:曾国藩:天道酬勤,地道酬德,人道酬诚
  19. HTML字体以及图标字体iconfont、Font Awesome的使用
  20. 如何使用ag-Grid

热门文章

  1. pwm调速流程图小车_循迹+pwm调速的小车源程序
  2. 如何修改图片分辨率为300dpi?怎么样修改照片的分辨率?
  3. 【心理咨询师考试笔记】操作技能(二)——心理评估
  4. Redis的攻击手法
  5. 2019113_房价预测
  6. 软件测试工程师面试的时候该怎么样介绍自己?
  7. hadoop 报错 there appears to be a gap in the edit log. we expected txitd 1, but got txid 14444
  8. 零基础学python pdf-零基础学Python PDF 全彩影印版
  9. 计算机应用基础考试试题及答案 在word中,用户建立的文件默认,自考计算机应用基础试题及参考答案...
  10. dns服务器理论基础知识