本节书摘来自华章计算机《深入理解Elasticsearch(原书第2版)》一书中的第1章,第1.2节,作者 [美]拉斐尔·酷奇(Rafal Ku)马雷克·罗戈任斯基(Marek Rogoziski),张世武 余洪淼 商旦 译,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1.2 何为Elasticsearch
当读者手持本书阅读时,可能已经对Elasticsearch有所了解了,至少已经了解了它的一些核心概念和基本用法。不过,为了全面理解该搜索引擎是如何工作的,我们最好简略地讨论一下它。
也许你已经了解到,Elasticsearch是一个可用于构建搜索应用的成品软件(译者注:区别于Lucene这种中间件)。它最早由Shay Banon创建,并于2010年2月发布。之后的几年,Elasticsearch迅速流行开来,成为其他开源和商业解决方案之外的一个重要选择。它是下载量最多的开源项目之一。
1.2.1 Elasticsearch的基本概念
现在,让我们浏览一下Elasticsearch的基本概念以及它们的特征。
1. 索引
Elasticsearch将它的数据存储在一个或者多个索引(index)中。用SQL领域的术语来类比,索引就像数据库,可以向索引写入文档或者从索引中读取文档。就像之前说过的那样,Elasticsearch在内部使用Lucene将数据写入索引或从索引中检索数据。读者需要注意的是,Elasticsearch中的索引可能由一个或多个Lucene索引构成,细节由Elasticsearch的索引分片(shard)、复制(replica)机制及其配置决定。
2. 文档
文档(document)是Elasticsearch世界中的主要实体(对Lucene来说也是如此)。对于所有使用Elasticsearch的案例来说,它们最终都会被归结到对文档的搜索之上。文档由字段构成,每个字段包含字段名以及一个或多个字段值(在这种情况下,该字段被称为是多值的,即文档中有多个同名字段)。文档之间可能有各自不同的字段集合,文档并没有固定的模式或强制的结构。这种现象看起来很眼熟(这些规则也适用于Lucene文档)。事实上,Elasticsearch的文档最后都被存储为Lucene文档了。从客户端的角度来看,文档是一个JSON对象(想了解更多关于JSON格式的细节,请参考http://en.wikipedia.org/wiki/JSON)。
3. 类型
Elasticsearch中每个文档都有与之对应的类型(type)定义。这允许用户在一个索引中存储多种文档类型,并为不同文档类型提供不同的映射。如果同SQL领域类比,Elasticsearch的类型就像一个数据库表。
4. 映射
正如你在1.1节所了解到的那样,所有文档在写入索引前都将被分析。用户可以设置一些参数,决定如何将输入文本分割为词条,哪些词条应该被过滤掉,或哪些附加处理有必要被调用(例如移除HTML标签)。这就是映射(mapping)扮演的角色:存储分析链所需的所有信息。虽然Elasticsearch能根据字段值自动检测字段的类型,有时候(事实上几乎是所有时候)用户还是想自己来配置映射,以避免出现一些令人不愉快的意外。
5. 节点
单个的Elasticsearch的服务实例被称为节点(node)。很多时候部署一个Elasticsearch节点就足以应付大多数简单的应用,但是考虑到容错性或者数据膨胀到单机无法应付这些状况,也许你会更倾向于使用多节点的Elasticsearch集群。
Elasticsearch节点可以按用途分为3类。众所周知,Elasticsearch是用来索引和查询数据的,因此第1类节点就是数据(data)节点,用来持有数据,提供对这些数据的搜索功能。第2类节点指主(master)节点,作为监督者负责控制其他节点的工作。一个集群中只有一个主节点。第3类节点是部落(tribe)节点。部落节点是Elasticsearch 1.0版新引入的节点类型,它可以像桥梁一样连接起多个集群,并允许我们在多个集群上执行几乎所有可以在单集群Elasticsearch上执行的功能。
6. 集群
多个协同工作的Elasticsearch节点的集合被称为集群(cluster)。Elasticsearch的分布式属性使我们可以轻松处理超过单机负载能力的数据量。同时,集群也是无间断提供服务的一种解决方案,即便当某些节点因为宕机或者执行管理任务(例如升级)不可用时,Elasticsearch几乎是无缝集成了集群功能。在我们看来,这是它胜过竞争对手的最主要优点之一。在Elasticsearch中配置一个集群是再容易不过的事了。
7. 分片
正如我们之前提到的那样,集群允许系统存储的数据总量超过单机容量。为了满足这个需求,Elasticsearch将数据散布到多个物理的Lucene索引上去。这些Lucene索引被称为分片(shard),而散布这些分片的过程叫做分片处理(sharding)。Elasticsearch会自动完成分片处理,并且让用户看来这些分片更像是一个大索引。请记住,除了Elasticsearch本身自动进行分片处理外,用户为具体的应用进行参数调优也是至关重要的,因为分片的数量在索引创建时就被配置好了,之后无法改变,除非创建一个新索引并重新索引全部数据。
8. 副本
分片处理允许用户推送超过单机容量的数据至Elasticsearch集群。副本(replica)则解决了访问压力过大时单机无法处理所有请求的问题。思路是很简单的,为每个分片创建冗余的副本,处理查询时可以把这些副本当作最初的主分片(primary shard)使用。值得注意的是,副本给Elasticsearch带来了更多的安全性。如果主分片所在的节点宕机了,Elasticsearch会自动从该分片的副本中选出一个作为新的主分片,因此不会对索引和搜索服务产生干扰。可以在任意时间点添加或移除副本,所以一旦你有需要,可随时调整副本的数量。
1.2.2 Elasticsearch架构背后的关键概念
Elasticsearch的架构遵循了一些设计理念。开发团队希望这个搜索引擎产品易于使用和扩展,这些特征在Elasticsearch的每个角落里都可以被看到。从架构的视角来看,有下面这些主要特征:
- 合理的默认配置,使得用户在简单安装以后能直接使用Elasticsearch而不需要任何额外的调优,这其中包括内置的发现(例如,字段类型检测)和自动配置功能。
- 默认的分布式工作模式。每个节点总是假定自己是某个集群的一部分或将是某个集群的一部分。
- 对等架构(P2P)可以避免单点故障。节点会自动连接到集群中的其他节点,进行相互的数据交换和监控操作。这其中就包括了索引分片的自动复制。
- 容易扩充新节点至集群,不论是从数据容量的角度还是数量的角度。
- Elasticsearch没有对索引中的数据结构强加任何限制。这允许用户调整现有的数据模型。正如之前我们所描述的那样,Elasticsearch支持在一个索引中存在多种数据类型,允许用户调整业务模型,包括处理文档之间的关联(尽管这种功能非常有限)。
- 准实时(near real time searching)搜索和版本同步(versioning)。考虑到Elasticsearch的分布式特性,查询延迟和节点之间临时的数据不同步是难以避免的。Elasticsearch尝试减少这些问题,并且提供了额外的机制用于版本同步。
1.2.3 Elasticsearch的工作流程
本节我们将探索一些关键的Elasticsearch特性,如启动、故障检测、数据索引和查询等。
1. 启动过程
当Elasticsearch节点启动时,它使用发现(discovery)模块来发现同一个集群中的其他节点(这里的关键是配置文件中的集群名称)并与它们连接。默认情况下,Elasticsearch节点会向网络中发送广播请求,以找到拥有相同集群名称的其他节点。读者可以通过下图的描述来了解相关的处理。
集群中有一个节点被选为主(master)节点。该节点负责集群的状态管理以及在集群拓扑变化时做出反应,分发索引分片至集群的相应节点上去。
请记住,从用户的角度来看,Elasticsearch中的管理节点并不比其他节点重要,这与其他的某些分布式系统不同(例如数据库)。在实践中,你不需要知道哪个节点是管理节点,所有操作可以发送至任意节点,Elasticsearch内部会自行处理这些不可思议的事情。如果有需要,任意节点可以并行发送子查询给其他节点,并合并搜索结果,然后返回给用户。所有这些操作并不需要经过管理节点处理(请记住,Elasticsearch是基于对等架构的)。
管理节点读取集群的状态信息,如果有必要,它会进行恢复(recovery)处理。在该阶段,管理节点会检查有哪些索引分片,并决定哪些分片将用作主分片。此后,整个集群进入黄色状态。
这意味着集群可以执行查询,但是系统的吞吐量以及各种可能的状况是未知的(这种状况可以简单理解为所有的主分片已经被分配了,但是副本没有被分配)。下面的事情就是寻找到冗余的分片用作副本。如果某个主分片的副本数过少,管理节点将决定基于某个主分片创建分片和副本。如果一切顺利,集群将进入绿色状态(这意味着所有主分片以及副本均已分配好)。
2. 故障检测
集群正常工作时,管理节点会监控所有可用节点,检查它们是否正在工作。如果任何节点在预定义的超时时间内不响应,则认为该节点已经断开,然后错误处理过程开始启动。这意味着可能要在集群–分片之间重新做平衡,选择新的主节点等。对每个丢失的主分片,一个新的主分片将会从原来的主分片的副本中选出来。新分片和副本的放置策略是可配置的,用户可以根据具体需求进行配置。更多的信息可以在第7章了解到。
为了描述故障检测(failure detection)是如何工作的,我们用一个只有3个节点的集群作为例子,将会有一个管理节点,两个数据节点。管理节点会发送ping请求至其他节点,然后等待响应。如果没有响应(实际上多少次ping请求无响应可以确认节点失败取决于配置),则该节点会被从集群中移除出去。相反地,所有节点也会向主节点发送ping请求来检查主节点是否在正常工作。节点之间的相互探测如下图所示。
3. 与Elasticsearch通信
前面已经讨论过Elasticsearch是如何构建的了,然而,对普通用户来说,最重要的部分是如何向Elasticsearch推送数据以及构建查询。为了提供这些功能,Elasticsearch对外公开了一个设计精巧的API。如果我们说,基本上每个Elasticsearch功能模块都有一个API,这将是令人鼓舞的。这个主API是基于REST的(REST细节请参考http://en.wikipedia.org/wiki/Representational_state_transfer),并且在实践中能轻松整合到任意支持HTTP协议的系统中去。
Elasticsearch假设数据由URL携带或者以JSON(JSON细节请参考(http://en.wikipedia.org/wiki/JSON)文档的形式由HTTP消息体携带。使用Java或者基于JVM语言的用户,应该了解一下Java API,它除了REST API提供的所有功能以外还有内置的集群发现功能。
值得一提的是,Elasticsearch在内部也使用Java API进行节点间通信。因此,Java API提供了所有可被REST API调用的功能。
(1)索引数据
Elasticsearch提供了多种索引数据的方式。最简单的方式是使用索引API,它允许用户发送一个文档至特定的索引。例如,使用curl工具(curl细节请参考http://curl.haxx.se/) ,可以使用如下命令创建一个文档:
第2种方式允许用户通过bulk API或UDP bulk API一次发送多个文档至集群。两者的区别在于网络连接方式,前者使用HTTP协议,后者使用UDP协议。后者速度快,但是不可靠。还有一种方式使用被叫作河流(river)的插件来发送数据。不过在这里我们不需要了解这种河流插件,因为它们将在Elasticsearch未来版本中被移除。
有一件事情需要记住,建索引操作只会发生在主分片上,而不是副本上。当一个索引请求被发送至一个节点上时,如果该节点没有对应的主分片或者只有副本,那么这个请求会被转发到拥有正确的主分片的节点。然后,该节点将会把索引请求群发给所有副本,等待它们的响应(这一点可以由用户控制),最后,当特定条件具备时(比如说达到规定数目的副本都完成了更新时)结束索引过程。
下图展示了我们刚刚探讨的索引处理过程。
(2)查询数据
查询API占据了Elasticsearch API的大部分。使用查询DSL(基于JSON的可用于构建复杂查询的语言),我们可以做下面这些事情:
- 使用各种查询类型,包括,简单的词项查询,短语查询,范围查询,布尔查询,模糊查询,区间查询,通配符查询,空间查询,以及具备人类可读的打分控制功能的函数查询,等等。
- 组合简单查询构建复杂查询。
- 文档过滤,在不影响评分的前提下抛弃那些不满足特定查询条件的文档。这一点可以提升性能。
- 查找与特定文档相似的文档。
- 查找特定短语的查询建议和拼写检查。
- 使用切面构建动态导航和计算各种统计量。
- 使用预搜索(prospective search)和查找与指定文档匹配的query集合。
谈到查询操作,读者应该了解一个很重要的事实:查询并不是一个简单的、单步骤的操作。一般来说,查询分为两个阶段:分散阶段(scatter phase)和合并阶段(gather phase)。在分散阶段将查询分发到包含相关文档的多个分片中去执行查询,而在合并阶段则从众多分片中收集返回结果,然后对它们进行合并、排序,进行后续处理,然后返回给客户端。该机制可以由下图描述。
Elasticsearch对外提供了6个系统参数,通过使用其中之一来定制分散/合并机制。在本书的姐妹版《Elasticsearch Server, Second Edition》(Packt出版社)中已经讨论过这个问题了。