Elasticsearch分布式搜索引擎介绍
Elasticsearch简介
Elasticsearch是一个近实时的分布式搜索分析引擎,常被用作全文搜索,结构化搜索,分析等。它使用 Java 编写的且开源,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单,通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。
Lucene 是一个基于Java的全文信息检索工具库,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。Lucene 目前是Apache Jakarta家族中的一个开源项目。也是目前最为流行的基于 Java 开源全文检索工具库。
然而,Elasticsearch 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎。 它可以被下面这样准确的形容:
- 一个分布式的实时文档存储,每个字段可以被索引与搜索
- 一个分布式近实时分析搜索引擎
- 能胜任上百个服务节点的扩展,并支持PB级别的结构化或者非结构化数据
基本概念
cluster集群:一个或多个节点的集合,通过启动时指定名字作为唯一标识,默认cluster-state
node节点:启动的ES的单个实例,保存数据并具有索引和搜索的能力,通过名字在集群中唯一标识,默认node-n
index索引:具有相似特点的文档的集合,可以对应为关系型数据库中的数据库,通过名字在集群内唯一标识。可以对应为Mysql中的数据库。
type文档类别:索引内部的逻辑分类,ES 6.x 版本中,一个索引只允许一个type,不再支持多个type。7.x版本中,type将废弃。可以对应为Mysql数据库中的表。
document文档:构成索引的最小单元,属于一个索引的某个类别,通过id 在Type 内唯一标识。可以对应Mysql数据库的表中的行。
field字段:构成文档的单元。可以对应Mysql数据库的表中的列。
mapping索引映射:用来约束文档字段的类型,可以理解为索引内部结构。可以对应Mysql数据库的表中每列的类型。
shard分片:将索引分为多个块,每块叫做一个分片。索引定义时需要指定分片数且不能更改,默认一个索引有5个分片,每个分片都是一个功能完整的Index,分片带来规模上(数据水平切分)和性能上(并行执行)的提升,是ES数据存储的最小单位。
replicas分片的备份:每个分片默认一个备份分片,它可以提升节点的可用性,同时能够提升搜索时的并发性能(搜索可以在全部分片上并行执行)
Elasticsearch特点
倒排索引
索引是现代搜索引擎的核心,建立索引的过程就是把源数据处理成非常方便查询的索引文件的过程。
为什么索引这么重要呢,试想你现在要在大量的文档中搜索含有某个关键词的文档,那么如果不建立索引的话你就需要把这些文档顺序的读入内存,然后检查这个文章中是不是含有要查找的关键词,这样的话就会耗费非常多的时间,想想搜索引擎可是在毫秒级的时间内查找出要搜索的结果的。这就是由于建立了索引的原因,你可以把索引想象成这样一种数据结构,他能够使你快速的随机访问存储在索引中的关键词,进而找到该关键词所关联的文档。
Lucene 采用的是一种称为倒排索引(inverted index)的机制。反向索引就是说我们维护了一个词 / 短语表,对于这个表中的每个词 / 短语,都有一个链表描述了有哪些文档包含了这个词 / 短语。这样在用户输入查询条件的时候,就能非常快的得到搜索结果。我们将在本系列文章的第二部分详细介绍 Lucene 的索引机制,由于 Lucene 提供了简单易用的 API,所以即使读者刚开始对全文本进行索引的机制并不太了解,也可以非常容易的使用 Lucene 对你的文档实现索引。
对文档建立好索引后,就可以在这些索引上面进行搜索了。搜索引擎首先会对搜索的关键词进行解析,然后再在建立好的索引上面进行查找,最终返回和用户输入的关键词相关联的文档。
我们来看下如下 2 个文档是如何被倒排索引的:
文档 1(Doc 1): Insight Data Engineering Fellows Program
文档 2(Doc 2): Insight Data Science Fellows Program
词项 | 文档 |
---|---|
data | Doc1,Doc2 |
engineering | Doc1 |
fellows | Doc1,Doc2 |
insight | Doc1,Doc2 |
program | Doc1,Doc2 |
science | Doc2 |
全文检索
全文检索首先将要查询的目标文档中的词提取出来,组成索引,通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
全文搜索两个最重要的方面是:
相关性(Relevance)
它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是 TF/IDF 方法(检索词频率/反向文档频率)、地理位置邻近、模糊相似,或其他的某些算法。
分析(Analysis)
它是将文本块转换为有区别的、规范化的token的一个过程,目的是为了创建倒排索引以及查询倒排索引。
分析主要包含下面的过程:
1,将一块文本分成适合于倒排索引的独立的词条(例如遇见空格和标点来分词)
2,将这些词条统一化为标准格式(例如小写化Quick,删除词条像 a,and,the等无用词,增加词条像jump和leap这种同义词等)以提高它们的"可搜索性"
结构化搜索
结构化搜索(Structured search)是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作,例如做大小对比等。
在结构化查询中,我们得到的结果总是非是即否,要么存于集合之中,要么存在集合之外。结构化查询不关心文件的相关度或评分;它简单的对文档包括或排除处理。
面向文档
Elasticsearch是面向文档的,意味着它存储整个对象或文档,这里的文档可以指一个HTML页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。Elasticsearch不仅存储文档,而且索引每个文档的内容,使之可以被检索。在Elasticsearch中,我们对文档进行索引、检索、排序和过滤,而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。
Elasticsearch 使用 JavaScript Object Notation(或者 JSON)作为文档的序列化格式。JSON 序列化为大多数编程语言所支持,并且已经成为 NoSQL 领域的标准格式。 它简单、简洁、易于阅读。
下面这个 JSON 文档代表了一个简单的 user 对象:
{
"email": "[email protected]",
"first_name": "John",
"last_name": "Smith",
"join_date": "2014/05/01"
}
海量数据近实时检索
虽然 Elasticsearch 中的变更不能立即可见,它还是提供了一个近实时的搜索引擎。 Lucene 的变更到磁盘是一个代价昂贵的操作。为了避免在文档对查询依然有效的时候,提交变更到磁盘,Elasticsearch 在内存缓冲和磁盘之间提供了一个文件系统缓存。内存缓存 (默认情况下) 每 1 秒刷新一次,在文件系统缓存中使用倒排索引创建一个新的段。这个段是开放的并对搜索有效。
文件系统缓存可以拥有文件句柄,文件可以是开放的、可读的或者是关闭的,但是它存在于内存之中。因为刷新间隔默认是 1 秒,变更不能立即可见,所以说是近实时的。因为 translog 是尚未落盘的变更持久化记录,它能有助于 CRUD 操作方面的近实时性。对于每次请求来说,在查找相关段之前,任何最近的变更都能从 translog 搜索到,因此客户端可以访问到所有的近实时变更。
你可以在创建 / 更新 / 删除操作后显式地刷新索引,使变更立即可见,但我并不推荐你这样做,因为这样会创建出来非常多的小 segment 而影响搜索性能。
Elasticsearch节点类型
一个 Elasticsearch实例是一个节点,一组节点组成了集群。Elasticsearch 集群中的节点可以配置为 3 种不同的角色:
主节点:控制 Elasticsearch 集群,负责集群中的操作,比如创建 / 删除一个索引,跟踪集群中的节点,分配分片到节点。主节点处理集群的状态并广播到其他节点,并接收其他节点的确认响应。每个节点都可以通过设定配置文件elasticsearch.yml中的node.master属性为true(默认) 成为主节点。对于大型的生产集群来说,推荐使用一个专门的主节点来控制集群,该节点将不处理任何用户请求。
数据节点:持有数据和倒排索引。默认情况下,每个节点都可以通过设定配置文件elasticsearch.yml中的node.data属性为true(默认) 成为数据节点。如果我们要使用一个专门的主节点,应将其node.data属性设置为false。
- 客户端节点:如果我们将node.master属性和node.data属性都设置为false,那么该节点就是一个客户端节点,扮演一个负载均衡的角色,将到来的请求路由到集群中的各个节点。
Elasticsearch集群状态
green:所有的主分片和副本分片都正常运行。
yellow:所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red:有主分片没能正常运行。
Elasticsearch集群选举
Elasticsearch使用自己开发的zen discovery算法来选举集群的master。大概原理如下:
1,对所有可以成为master的节点根据nodeId排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。
2,如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举。
3,对于脑裂问题,需要把候选master节点最小值设置为可以成为master节点数n/2+1(quorum )
Elasticsearch写操作
当我们发送索引一个新文档的请求到协调节点后,将发生如下一组操作:
- Elasticsearch集群中的每个节点都包含了改节点上分片的元数据信息。协调节点 (默认)使用文档ID参与计算,以便为路由提供合适的分片。Elasticsearch使用MurMurHash4函数对文档ID进行哈希,其结果再对分片数量取模,得到的结果即是索引文档的分片。
公式:shard = hash(document_id) % (num_of_primary_shards)- 当分片所在的节点接收到来自协调节点的请求后,会将该请求写入 translog(我们将在本系列接下来的文章中讲到),并将文档加入内存缓冲。如果请求在主分片上成功处理,该请求会并行发送到该分片的副本上。当translog 被同步( fsync ) 到全部的主分片及其副本上后,客户端才会收到确认通知。
- 内存缓冲会被周期性刷新 (默认是 1 秒),内容将被写到文件系统缓存的一个新段上。虽然这个段并没有被同步 (fsync),但它是开放的,内容可以被搜索到。
- 每 30 分钟,或者当 translog 很大的时候,translog 会被清空,文件系统缓存会被同步。这个过程在 Elasticsearch 中称为冲洗 (flush)。在冲洗过程中,内存中的缓冲将被清除,内容被写入一个新段。段的 fsync 将创建一个新的提交点,并将内容刷新到磁盘。旧的 translog 将被删除并开始一个新的 translog。
Elasticsearch更新和删除操作
删除和更新也都是写操作。但是 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示其变更。那么,该如何删除和更新文档呢?
磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并 (我们将在本系列接下来的文章中讲到) 时,在.del文件中被标记为删除的文档将不会被写入新段。
接下来我们看更新是如何工作的。在新的文档被创建时,Elasticsearch 会为该文档指定一个版本号。当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
Elasticsearch读操作
读操作包含 2 部分内容:
1,查询阶段
2,提取阶段
查询阶段
在这个阶段,协调节点会将查询请求路由到索引的全部分片 (主分片或者其副本) 上。每个分片独立执行查询,并为查询结果创建一个优先队列,以相关性得分排序 (我们将在本系列的后续文章中讲到)。全部分片都将匹配文档的 ID 及其相关性得分返回给协调节点。协调节点创建一个优先队列并对结果进行全局排序。会有很多文档匹配结果,但是,默认情况下,每个分片只发送前 10 个结果给协调节点,协调节点为全部分片上的这些结果创建优先队列并返回前 10 个作为 hit。
提取阶段
当协调节点在生成的全局有序的文档列表中,为全部结果排好序后,它将向包含原始文档的分片发起请求。全部分片填充文档信息并将其返回给协调节点。
Elasticsearch故障转移
故障检测原理
master 节点会 ping所有其他节点,以检查它们是否还活着;然后所有节点 ping 回去,告诉 master 他们还活着。
故障转移
我们关闭一个主节点Node1。而集群必须拥有一个主节点来保证正常工作,所以发生的第一件事情就是选举一个新的主节点:Node 2。
在我们关闭 Node 1的同时也失去了主分片1和2,并且在缺失主分片的时候索引也不能正常工作。如果此时来检查集群的状况,我们看到的状态将会为 red :不是所有主分片都在正常工作。我看看到在其它节点上存在着这两个主分片的完整副本, 所以新的主节点立即将这些分片在 Node 2 和 Node 3 上对应的副本分片提升为主分片, 此时集群的状态将会为 yellow 。 这个提升主分片的过程是瞬间发生的,如同按下一个开关一般。为什么我们集群状态是yellow而不是green呢?虽然我们拥有所有的三个主分片,但是同时设置了每个主分片需要对应2份副本分片,而此时只存在一份副本分片。所以集群不能为 green 的状态。(如果我们设置的副本为1,则集群状态会变为green)
- 如果我们重新启动 Node 1 ,集群可以将缺失的副本分片再次进行分配,那么集群的状态也将完全正常,恢复green状态。 如果 Node 1 依然拥有着之前的分片,它将尝试去重用它们,同时仅从主分片复制发生了修改的数据文件。
Elasticsearch优化点
1,单节点内存不超过32G,使用jvm压缩指针
2,合理设置索引template,包括settings和mappings
3,使用ssd提升磁盘io
4,数据量大的时候,使用用from,size来深层分页非常消耗性能,应该禁止使用
5,关闭不使用的索引
6,留一半的系统内存供文件系统缓存使用
7,使用bulk写入
8,增大实时性要求不高的索引refresh时间
9,关闭系统swap
10,整个集群重启情况下,延迟分片策略按照需要调整
整个集群重启,恢复慢问题
原因:
1,需要加载所有active状态index的translog,保证主shard数据完整性主shard恢复之后,rep从主shard复制缺少的部分
2,shard过多
缓解:
1,关闭不需要的索引
2,重启前flush集群的translog
3,集群恢复的时候,关闭集群写入,降低集群系统压力
无法分配的shard
原因:
1,文件损坏或者文件系统问题
2,机器压力大导致,allocation分配超时
工具:
1,可以使用explain API具体查看分配失败的原因
解决:
1,开启关闭索引,触发重新分配
2,设置rep为0
3,使用rerouting API,可能有丢数据风险
Elasticsearch主要特性
高级特性
1,支持类似SQL查询
2,机器学习
最新稳定7.x新特性
1,默认分片调整为1
2,无type索引结构
3,kibana支持暗黑模式
4,使用Term查询性能提升3700%
5,对内存管理更加健壮,降低OOM发生
6,时间戳支持纳秒级别