Elasticsearch 基本概念和原理
简介
Elasticsearch 是一款基于 Lucene 的高扩展性分布式全文搜索引擎,是整个 ELK STACK 的核心。通过 RESTful API(一种接口设计规范,让接口更易懂)将 Lucene 原本的复杂性做了隐藏。
ES 支持对数据进行分布式、实时分析,并且可以进行搜索补全、关键词高亮、自动纠错、分词搜索等功能。相比使用 SQL 语句在数据库中进行查询,ES 通过倒排索引和分词功能快速对数据页进行定位,然后再用正排索引通过数据页进行查找,搜索速度更快,就算是 TB、PB 级别的数据也可以快速搜索。
- 正排索引:从文档到关键字的映射(已知文档求关键字),正排表是以文档的 ID 为关键字,表中记录文档中每个字的位置信息,查找时扫描表中每个文档中字的信息直到找出所有包含查询关键字的文档。
- 倒排索引:从关键字到文档的映射(已知关键字求文档),倒排表以字或词为关键字进行索引,表中关键字所对应的记录表项记录了出现这个字或词的所有文档,一个表项就是一个字表段,它记录该文档的 ID 和字符在该文档中出现的位置情况。
ES 本身也是一个分布式存储系统,如同其他分布式系统一样,我们经常关注的一些特性如下。
- 数据可靠性:通过分片副本和事务日志机制保障数据安全。
- 服务可用性:在可用性和一致性的取舍方面,默认情况下 ES 更倾向于可用性,只要主分片可用即可执行写入操作。
- 一致性:笔者认为是弱一致性。只要主分片写成功,数据就可能被读取。因此读取操作在主分片和副分片上可能会得到不同结果。
- 原子性:索引的读写、别名更新是原子操作,不会出现中间状态。但 bulk 不是原子操作,不能用来实现事务。
- 扩展性:主副分片都可以承担读请求,分担系统负载。
术语
- 节点(node):站在用户的角度,ES 并没有主从节点概念,对用户来说每个节点都可以响应请求。但是对于集群来说会通过一个主节点来管理每个节点的状态、决定分片(Shared)的分布方式以及周期性检查其他节点是否可用并进行修复。各节点是通过集群名称来判断是否属于同一节点
- 索引(index):存放 ES 数据的集合,类似 MySQL 中的库、Index 里的 Types 相当于表,从 ES6 开始一个 Index 下只能包含一个 Type,从 ES 7.X 开始 Type 的概念已经被删除了
- Document(文档): 是 index 中的行,许多条 Document 构成了一个 Index。
- 分片(shared):Elasticsearch 7 以前在创建索引时如果没有指定分片数量(number_of_shards),默认会分为 5 个分片和一个副本(number_of_replicas),这些分片会依据内部算法划分到不同的节点中去。从 ES7 以后默认分片数修改为了 1 个。分片可以带来并行执行的好处,能提高效率。文档数据是根据 HASH 算法存储到分片,由于算法中涉及到了分片的数量,所以分片创建好以后不可修改,否则 HASH 值就变了。每一个分片都是一个独立完整的索引,分布在不同的节点上。如果发现有分片没有被分配到节点上,可以看下是否磁盘没有空间。如果节点数>分片数并不能起到优化作用,所以在初期就要规划好分片数
- 副本(Replication):副本的存在是为了提高数据的安全性,副本数量通常设置为集群节点数-1,有了副本的存在就能保证不会因为某个节点的故障而丢失数据,当有节点发生故障时,会从副本中选取一个分片提升为主分片。在使用 head 或 cerebro 插件查看集群状态时,如果是虚线边框就代表是副本分片,实线边框是主分片
- 字段(Field):相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
- 映射 mapping:mapping 是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的,其它就是处理 es 里面数据的一些使用规则设置也叫做映射,按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好。相当于 mysql 中的创建表的过程,设置主键外键、字段类型等等。
模块
Cluster
Cluster 模块是主节点执行集群管理的封装实现,管理集群状态,维护集群层面的配置信息。主要功能如下:
- 管理集群状态,将新生成的集群状态发布到集群所有节点。
- 调用 allocation 模块执行分片分配,决策哪些分片应该分配到哪个节点
- 在集群各节点中直接迁移分片,保持数据平衡。
Allocation
封装了分片分配相关的功能和策略,包括主分片的分配和副分片的分配,本模块由主节点调用。创建新索引、集群完全重启都需要分片分配的过程。
Discovery
发现模块负责发现集群中的节点,以及选举主节点。当节点加入或退出集群时,主节点会采取相应的行动。从某种角度来说,发现模块起到类似 ZooKeeper 的作用,选主并管理集群拓扑。
Gateway
负责对收到 Master 广播下来的集群状态(cluster state)数据的持久化存储,并在集群完全重启时恢复它们。
Indices
索引模块管理全局级的索引设置,不包括索引级的(索引设置分为全局级和每个索引级)。它还封装了索引数据恢复功能。集群启动阶段需要的主分片恢复和副分片恢复就是在这个模块实现的。
HTTP
HTTP 模块允许通过 JSON over HTTP 的方式访问 ES 的 API,HTTP 模块本质上是完全异步的,这意味着没有阻塞线程等待响应。使用异步通信进行 HTTP 的好处是解决了 C10k 问题(10k 量级的并发连接)。
在部分场景下,可考虑使用 HTTP keepalive 以提升性能。注意:不要在客户端使用 HTTP chunking。
Transport
传输模块用于集群内节点之间的内部通信。从一个节点到另一个节点的每个请求都使用传输模块。
如同 HTTP 模块,传输模块本质上也是完全异步的。
传输模块使用 TCP 通信,每个节点都与其他节点维持若干 TCP 长连接。内部节点间的所有通信都是本模块承载的。
Engine
Engine 模块封装了对 Lucene 的操作及 translog 的调用,它是对一个分片读写操作的最终提供者。
ES 使用 Guice 框架进行模块化管理。Guice 是 Google 开发的轻量级依赖注入框架(IoC)。
读写原理
建立索引和执行搜索的原理如下图所示:
写入流程
- 客户端向 NODE1 发送写请求。
- NODE1 使用文档 ID 来确定文档属于分片 0,通过集群状态中的内容路由表信息获知分片 0 的主分片位于 NODE3,因此请求被转发到 NODE3 上。
- NODE3 上的主分片执行写操作。如果写入成功,则它将请求并行转发到 NODE1 和 NODE2 的副分片上,等待返回结果。当所有的副分片都报告成功,NODE3 将向协调节点报告成功,协调节点再向客户端报告成功。
读取流程
ES 的读取分为 GET 和 Search 两种操作,这两种读取操作有较大的差异,GET/MGET 必须指定三元组:_index、_type、_id。 也就是说,根据文档 id 从正排索引中获取内容。而 Search 不指定_id,根据关键词从倒排索引中获取内容。
GET 流程
- 客户端向 NODE1 发送读请求。
- NODE1 使用文档 ID 来确定文档属于分片 0,通过集群状态中的内容路由表信息获知分片 0 有三个副本数据,位于所有的三个节点中,此时它可以将请求发送到任意节点,这里它将请求转发到 NODE2。
- NODE2 将文档返回给 NODE1,NODE1 将文档返回给客户端。
NODE1 作为协调节点,会将客户端请求轮询发送到集群的所有副本来实现负载均衡。
在读取时,文档可能已经存在于主分片上,但还没有复制到副分片。在这种情况下,读请求命中副分片时可能会报告文档不存在,但是命中主分片可能成功返回文档。一旦写请求成功返回给客户端,则意味着文档在主分片和副分片都是可用的。
Search 流程
在协调节点,搜索任务被执行成一个两阶段过程,即 query then fetch。真正执行搜索任务的节点称为数据节点。
需要两个阶段才能完成搜索的原因是,在查询的时候不知道文档位于哪个分片,因此索引的所有分片(某个副本)都要参与搜索,然后协调节点将结果合并,再根据文档 ID 获取文档内容。例如,有 5 个分片,查询返回前 10 个匹配度最高的文档,那么每个分片都查询出当前分片的 TOP 10,协调节点将 5×10 = 50 的结果再次排序,返回最终 TOP 10 的结果给客户端。
Query 阶段
- 客户端发送 search 请求到 NODE 3。
- Node 3 将查询请求转发到索引的每个主分片或副分片中。
- 每个分片在本地执行查询,并使用本地的 Term/Document Frequency 信息进行打分,添加结果到大小为 from + size 的本地有序优先队列中。
- 每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,协调节点合并这些值到自己的优先队列中,产生一个全局排序后的列表。
协调节点广播查询请求到所有相关分片时,可以是主分片或副分片,协调节点将在之后的请求中轮询所有的分片副本来分摊负载。
Fetch 阶段
- 协调节点向相关 NODE 发送 GET 请求。
- 分片所在节点向协调节点返回数据。
- 协调节点等待所有文档被取得,然后返回给客户端。
分片所在节点在返回文档数据时,处理有可能出现的_source 字段和高亮参数。
协调节点首先决定哪些文档“确实”需要被取回,例如,如果查询指定了{ "from": 90, "size":10 },则只有从第 91 个开始的 10 个结果需要被取回。为了避免在协调节点中创建的 number_of_shards * (from + size)优先队列过大,应尽量控制分页深度。
一致性
索引恢复
索引恢复过程可分为主分片恢复流程和副分片恢复流程。
- 主分片从 translog 中自我恢复,尚未执行 flush 到磁盘的 Lucene 分段可以从 translog 中重建;
- 副分片需要从主分片中拉取 Lucene 分段和 translog 进行恢复。但是有机会跳过拉取 Lucene 分段的过程。
索引恢复的触发条件包括从快照备份恢复、节点加入和离开、索引的_open 操作等。
主副分片一致
假设在第一阶段执行期间,有客户端索引操作要求将 docA 的内容写为 1,主分片执行了这个操作,而副分片由于尚未就绪所以没有执行。第二阶段期间客户端索引操作要求写 docA 的内容为 2,此时副分片已经就绪,先执行将 docA 写为 2 的新增请求,然后又收到了从主分片所在节点发送过来的 translog 重复写 docA 为 1 的请求该如何处理?具体流程如下图所示。
答案是在写流程中做异常处理,通过版本号来过滤掉过期操作。写操作有三种类型:索引新文档、更新、删除。索引新文档不存在冲突问题,更新和删除操作采用相同的处理机制。每个操作都有一个版本号,这个版本号就是预期 doc 版本,它必须大于当前 Lucene 中的 doc 版本号,否则就放弃本次操作。对于更新操作来说,预期版本号是 Lucene doc 版本号+1。主分片节点写成功后新数据的版本号会放到写副本的请求中,这个请求中的版本号就是预期版本号。
这样,时序上存在错误的操作被忽略,对于特定 doc,只有最新一次操作生效,保证了主副分片一致。
集群
分布式系统的集群方式大致可以分为主从(Master-Slave)模式和无主模式。ES、HDFS、HBase 使用主从模式,Cassandra 使用无主模式。主从模式可以简化系统设计,Master 作为权威节点,部分操作仅由 Master 执行,并负责维护集群元信息。缺点是 Master 节点存在单点故障,需要解决灾备问题,并且集群规模会受限于 Master 节点的管理能力。
集群角色
生产环境架构中每个节点配置单一职责,具体为 Master、Data、Ingest、Coordinate、Tribe 的一个,有利于每个角色可以使用不同的机器配置。
Master 主节点
负责集群层面的相关操作,管理集群变更。通过配置 node.master: true(默认)使节点具有被选举为 Master 的资源。主节点是全局唯一的。主节点也可以是数据节点,不太推荐。
生产环境通常配置 3 台,低配置(低 CPU 核数、小内存、低磁盘)
Data 数据节点
负责保存数据、执行数据相关操作:CRUD、搜索、聚合等。通过配置 node.data: true。(一般情况下,数据读写流程只和数据节点交互,不会和主节点打交道,异常情况除外)
高配置(高 CPU 核数、大内存、SSD 盘)
Ingest 预处理节点
负责写入和查询的数据进行预处理,在写入数据之前,通过事先定义好的一系列的 processors(处理器)和 pipeline(管道),对数据进行某种转换、富化。processors 和 pipeline 拦截 bulk 和 index 请求,在应用相关操作后将文档传回给 index 或 bulk API。
中配置(高 CPU 核数、中内存、低磁盘)
Coordinate 协调节点
通常在 es 大集群中配置,降低 Master 和 Data Nodes 的负载,负责接受请求、分发请求、汇总结果。应对客户的未知查询请求,深度聚合可能导致 OOM
中高配置(中高 CPU 核数、中高内存、低磁盘)
Tribe 部落节点
它不做主节点,也不做数据节点,仅用于路由请求,本质上是一个智能负载均衡器(从负载均衡器的定义来说,智能和非智能的区别在于是否知道访问的内容存在于哪个节点),从 5.0 版本开始,这个角色被协调节点(Coordinating only node)取代。
集群状态
集群健康状态分为三种:
- Green,所有的主分片和副分片都正常运行。
- Yellow,所有的主分片都正常运行,但不是所有的副分片都正常运行。这意味着存在单点故障风险。
- Red,有主分片没能正常运行。
集群扩容
当扩容集群、添加节点时,分片会均衡地分配到集群的各个节点,从而对索引和搜索过程进行负载均衡,这些都是系统自动完成的。
分片分配过程中除了让节点间均匀存储,还要保证不把主分片和副分片分配到同一节点,避免单个节点故障引起数据丢失。
当主节点异常时,集群会重新选举主节点。当某个主分片异常时,会将副分片提升为主分片。
参考
https://weread.qq.com/web/reader/f9c32dc07184876ef9cdeb6
https://www.linuxe.cn/post-295.html