不知道大家在项目中是否使用过 ElastricSearch?大家对它的了解又有多少呢?官网的定义:Elasticsearch 是一个分布式、可扩展、近实时的搜索与数据分析引擎。今天我们就来揭开一下它的神秘面纱(以下简称 ES)。
ES 是使用 Java 编写的一种开源搜索引擎,它在内部使用 Lucene 做索引与搜索,通过对 Lucene 的封装,隐藏了 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。然而,ES 不仅仅是 Lucene,并且也不仅仅只是一个全文搜索引擎,它还有如下特点:
@[toc]
首先我们将数据大体分为两类,一类是结构化数据:也称作行数据,指具有固定格式或有限长度的数据,是由二维表结构来逻辑表达和实现的数据,严格地遵循数据格式与长度规范,主要通过关系型数据库进行存储和管理,如数据库、元数据等;一类是非结构化数据:又可称为全文数据,不定长或无固定格式,不适于由数据库二维表来表现,包括所有格式的办公文档、XML、HTML、Word 文档,邮件,各类报表、图片和音频、视频信息等。
如果要更细致的区分的话,XML、HTML 可划分为半结构化数据。因为它们也具有自己特定的标签格式,所以既可以根据需要按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。
根据两种数据分类,搜索也相应的分为两种:
(1)结构化数据搜索:因为它们具有特定的结构,所以我们一般都是可以通过关系型数据库(MySQL,Oracle 等)的二维表(Table)的方式存储和搜索,也可以建立索引。
(2)非结构化数据搜索:即对全文数据的搜索,主要有两种方法:顺序扫描、全文搜索;
Lucene 是 apache 软件基金会 4 jakarta 项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene 的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene 是一套用于全文检索和搜寻的开源程式库,由 Apache 软件基金会支持和提供。
我所熟知的以 Lucene 为基础建立的开源可用全文搜索引擎主要有 Solr 和 ES:ES 本身就具有分布式的特性和易安装使用的特点,而 Solr 的分布式需要借助第三方来实现,例如通过使用 ZooKeeper 来达到分布式协调管理。
接下来我们说一下 Lucene 能实现全文搜索的一个重要的查询结构:倒排索引
举例
通过分词器将每个文档的内容域拆分成单独的词(我们称它为词条或 Term),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。结果
这种结构由文档中所有不重复词的列表构成,对于其中每个词都有一个文档列表与之关联。这种由属性值来确定记录的位置的结构就是倒排索引。带有倒排索引的文件我们称为倒排文件。
将上面的内容转换为图的形式来说明倒排索引的结构信息
概念如下:
词典和倒排表是 Lucene 中很重要的两种数据结构,是实现快速检索的重要基石。词典和倒排文件是分两部分存储的,词典在内存中而倒排文件存储在磁盘上。
为了大家更好的理解 ES,下面介绍一下 ES 中的概念:
(1)Near Realtime(NRT):近实时,一是从写入数据到数据可以被搜索到有一个小延迟(默认 1 秒);二是基于 ES 执行搜索和分析可以达到秒级;
(2)Cluster(集群):ES 集群由一个或多个 ES 节点组成,每个节点配置相同的 cluster.name (默认值为 “elasticsearch”)即可加入集群。确保不同的环境中使用不同的集群名称,否则最终会导致节点加入错误的集群。ES 的集群搭建很简单,不需要依赖第三方协调管理组件,自身内部就实现了集群的管理功能。
(3)Node:一个 ES 服务启动实例就是一个节点(Node)。节点通过 node.name 来设置节点名称,如果不设置则在启动时给节点分配一个随机通用唯一标识符作为名称(默认是随机分配的)。默认节点会去加入一个名称为“elasticsearch”的集群。如果直接启动一堆节点,那么它们会自动组成一个 elasticsearch 集群,当然一个节点也可以组成一个 elasticsearch 集群。
(4)Index(索引):简单理解就是一个数据库,索引有名称,包含一堆有相似结构的文档数据,一个 index 包含很多 document,一个 index 就代表了一类类似的或者相同的 document。比如说建立一个 product index,商品索引,里面可能就存放了所有的商品数据,所有的商品 document。索引是保存相关数据的地方。 索引实际上是指向一个或者多个物理 分片 的 逻辑命名空间 。
(5)Type(类型):简单理解就是一张表,每个索引里都可以有一个或多个 type,type 是 index 中的一个逻辑数据分类,一个 type 下的 document,都有相同的 field,比如博客系统,有一个索引,可以定义用户数据 type,博客数据 type,评论数据 type。
(6)Document(文档)&field(字段):Document 就是一行数据,es 中的最小数据单元,一个 document 可以是一条客户数据,一条商品分类数据,通常用 JSON 数据结构表示,每个 index 下的 type 中,都可以去存储多个 document。一个 document 里面有多个 field,每个 field 就是一个数据字段。
①节点的角色
节点分为主节点、候选主节点、数据节点和协调节点,其中候选主节点和数据节点是在配置文件中指定的,而主节点是由候选主节点选举出来的,协调节点则可以是任何节点,以下是它们的功能介绍:
数据节点:负责数据的存储和相关的操作,例如对数据进行增、删、改、查和聚合等操作,所以数据节点(Data 节点)对机器配置要求比较高,对 CPU、内存和 I/O 的消耗很大。通常随着集群的扩大,需要增加更多的数据节点来提高性能和可用性。
候选主节点:可以被选举为主节点(Master 节点),集群中只有候选主节点才有选举权和被选举权,其他节点不参与选举的工作。
主节点:负责创建索引、删除索引、跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点、追踪集群中节点的状态等。主节点和其他节点之间通过 Ping 的方式互相检查,主节点负责 Ping 所有其他节点,判断是否有节点已经挂掉;其他节点也通过 Ping 的方式判断主节点是否处于可用状态;稳定的主节点对集群的健康是非常重要的。
协调节点:虽然对节点做了角色区分,但是用户的请求可以发往任何一个节点,并由该节点负责分发请求、收集结果等操作,而不需要主节点转发,这种节点可称之为协调节点,协调节点是不需要指定和配置的,集群中的任何节点都可以充当协调节点的角色。
ES 中每个节点既可以是候选主节点也可以是数据节点,通过在配置文件 ../config/elasticsearch.yml 中设置即可,默认都为 true。
node.master: true //是否候选主节点node.data: true //是否数据节点
但是由于数据节点对 CPU、内存核 I/O 消耗都很大,所以如果某个节点既是数据节点又是主节点,那么可能会对主节点产生影响从而对整个集群的状态产生影响。因此为了提高集群的健康性,我们应该对 ES 集群中的节点做好角色上的划分和隔离,可以使用几个配置较低
的机器群作为候选主节点群。
②发现与选举机制
发现机制(Zen Discovery)
那么有一个问题,ES 内部是如何通过一个相同的设置 cluster.name 就能将不同的节点连接到同一个集群的呢?答案是 Zen Discovery
。Zen Discovery
是 ES 的内置默认发现模块(发现模块的职责是发现集群中的节点以及选举 Master 节点)。它提供单播和基于文件的发现,并且可以扩展为通过插件支持云环境和其他形式的发现。Zen Discovery
与其它模块集成:例如,节点之间的所有通信都使用 Transport 模块完成,节点使用发现机制通过 Ping 的方式查找其他节点。
ES 默认被配置为使用单播
发现,以防止节点无意中加入集群,只有在同一台机器上运行的节点才会自动组成集群。如果集群的节点运行在不同的机器上,使用单播,你可以为 ES 节点提供一些它应该去尝试连接的节点列表即单播列表。当该节点联系到单播列表中的成员时,它就会得到整个集群所有节点的状态,然后它会联系 Master 节点,并加入集群。这意味着单播列表不需要包含集群中的所有节点, 它只是需要足够的节点,当一个新节点联系上其中一个并且说上话就可以了。如果你使用 Master 候选节点作为单播列表,你只要列出三个就可以了。这个配置在 elasticsearch.yml 文件中:
discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
ES 节点启动后先 Ping ,如果 discovery.zen.ping.unicast.hosts 有设置,则 Ping 设置中的 Host ,否则尝试 ping 本地的几个端口。ES 支持同一个主机启动多个节点,Ping 的 Response 会包含该节点的基本信息以及该节点认为的 Master 节点。
Master 选举
采用分布式系统思路,保证选举出的 master 被多数派(quorum)的 master-eligible node(候选主节点)认可,以此来保证只有一个 master。这个 quorum 通过以下配置进行配置:
conf/elasticsearch.yml: discovery.zen.minimum_master_nodes: 2
(1)master 选举谁发起,什么时候发起?
master(主节点)选举当然是由 master-eligible(候选主节点)节点发起,当一个 master-eligible 节点发现满足以下条件时发起选举:
总结一句话,即当一个节点发现包括自己在内的多数派的 master-eligible 节点认为集群没有 master 时,就可以发起 master 选举。
(2)当需要选举 master 时,选举谁?
如果各节点都没有认为的 Master ,则从所有候选主节点中选择,规则很简单,按照 ID(ID 为节点第一次启动时随机生成) 的字典序排序,取第一个。之所以这么设计,应该是为了让选举结果尽可能稳定,不要出现都想当 master 而选不出来的情况。
(3) 什么时候选举成功?
这里有个限制条件就是 discovery.zen.minimum_master_nodes :如果候选主节点数达不到最小值的限制,则循环上述过程,直到候选主节点数足够可以开始选举,如果只有一个 Local 节点那就选出的是自己。如果当前节点被选为 Master ,则开始等待节点数达到 discovery.zen.minimum_master_nodes,然后提供服务;如果当前节点不是 Master ,则尝试加入 Master 。
举例说明:
当一个 master-eligible node(我们假设为 Node_A)发起一次选举时:
a、假设 Node_A 选 Node_B 当 Master:Node_A 会向 Node_B 发送 join 请求,那么此时:
b、假设 Node_A 选自己当 Master:
此时 NodeA 会等别的 node 来 join,即等待别的 node 的选票,当收集到超过半数的选票时,认为自己成为 master,然后变更 cluster_state 中的 master node 为自己,并向集群发布这一消息。
③脑裂现象
如果由于网络或其他原因导致集群中选举出多个 Master 节点,使得数据更新时出现不一致,这种现象称之为脑裂,即集群中不同的节点对于 Master 的选择出现了分歧,出现了多个 Master 竞争。
脑裂问题可能有以下几个原因造成:
为了避免脑裂现象的发生,我们可以从原因着手通过以下几个方面来做出优化措施:
阿 Q 将持续更新 java 实战方面的文章,如果你有不同的意见或者更好的 idea,欢迎联系阿 Q。
【阿 Q 说代码】,值得关注的公众号 文章风格多变,配图通俗易懂,故事生动有趣,来聊聊技术呀!
领取专属 10元无门槛券
私享最新 技术干货