Elasticsearch 的架构设计深受其底层搜索引擎 Apache Lucene 的影响,并结合了分布式系统、大数据处理等思想,旨在实现高可用、高扩展、高性能的搜索和分析能力。

我们可以从以下几个核心层面来理解它的技术架构:
- 顶层架构:集群与节点
- 数据模型:索引、类型与文档
- 核心数据结构:倒排索引
- 分布式架构:分片与副本
- 工作原理:写入与查询流程
- 高可用与故障转移机制
- 核心组件
顶层架构:集群与节点
Elasticsearch 本质上是一个分布式系统,其最顶层的概念是集群。
-
集群
- 一个或多个具有相同
cluster.name的节点组合在一起,共同存储数据并提供搜索服务。 - 集群提供了一个唯一的名称(默认为
elasticsearch),节点通过这个名称来发现并加入集群。 - 集群的状态由一个主节点(Master-Eligible Node)管理。
- 一个或多个具有相同
-
节点
(图片来源网络,侵删)-
一个 Elasticsearch 的实例就是一个节点。
-
节点启动后,会寻找具有相同
cluster.name的其他节点,并尝试加入它们所在的集群。 -
节点根据其角色和功能,可以被配置为不同的类型:
-
主节点:负责管理集群的元数据,如创建、删除索引,跟踪哪些节点在集群中,以及决定将分片分配给哪些节点。主节点不负责数据的读写操作,以确保其性能和稳定性,一个健康的集群必须有一个主节点。
(图片来源网络,侵删) -
数据节点:负责存储数据,执行数据的增、删、改、查以及聚合等操作,数据节点是集群中负载最重的节点。
-
客户端节点:不存储数据,也不负责主节点选举,它像一个“负载均衡器”,将来自客户端的请求路由到合适的数据节点或主节点,它可以将请求转发到其他节点,然后将结果汇总返回。
-
协调节点:在较新版本的 Elasticsearch 中,这个角色被弱化了,默认情况下,每个节点都可以作为协调节点,当一个节点接收到客户端请求时,它会扮演协调节点的角色,将请求分发给其他节点,最后将结果汇总返回。
-
最佳实践:在生产环境中,建议将角色分离,部署专用的主节点、数据节点和客户端节点,以实现更好的资源隔离和集群稳定性。
数据模型:索引、类型与文档
Elasticsearch 的数据模型与关系型数据库有类比关系,但更灵活。
| Elasticsearch | 关系型数据库 |
|---|---|
| 索引 | 数据库 |
| 类型 | 表 (在 7.x 版本后已弃用,未来将被移除) |
| 文档 | 行 |
-
文档
- Elasticsearch 中数据的基本单位,是一个 JSON 格式的对象。
- 文档是自我包含的,包含一个或多个字段及其值。
- 每个文档都有一个唯一的
_id,可以由用户指定或由 Elasticsearch 自动生成。
-
类型
- 在一个索引中,可以定义一种或多种类型,用于对文档进行逻辑分组,类似于数据库中的表。
- 重要:从 Elasticsearch 6.0 开始,一个索引只能有一个类型,从 7.0 开始,类型功能已被弃用,官方计划在未来版本中完全移除,这主要是为了简化数据模型和与 Lucene 的对齐。
-
索引
- 索引是具有相似特征的文档的集合,相当于一个数据库。
- 索引由一个或多个分片组成,是数据存储和索引的逻辑空间。
核心数据结构:倒排索引
这是 Elasticsearch 能够实现快速搜索的核心秘密武器,与传统的数据库(使用 B-Tree 等正排索引)不同,倒排索引是为全文检索优化的。
-
正排索引:通过文档 ID 找到文档内容,就像书的目录,告诉你第几页有什么内容。
doc_id -> [field1, field2, ...]
-
倒排索引:通过词条 找到包含该词条的文档列表,就像书的索引,告诉你某个词出现在哪些页。
term -> [doc_id1, doc_id5, doc_id9, ...]
倒排索引的组成:
- 词典:所有词条的有序集合,查询时,先在词典中快速定位词条。
- 倒排列表:与每个词条关联的列表,包含所有包含该词条的文档 ID,以及该词条在文档中的位置、词频 等信息。
优点:
- 极快的查询速度:查找包含特定词的文档非常高效。
- 支持全文搜索:可以轻松实现模糊匹配、通配符、短语搜索等复杂查询。
分布式架构:分片与副本
为了实现水平扩展和高可用,Elasticsearch 引入了分片 的概念。
-
分片
- 一个索引可以被拆分成多个部分,每个部分就是一个分片。
- 分片是 Elasticsearch 的最小工作单元,所有的数据都存储在分片中,而分片则分布在集群的各个数据节点上。
- 一个索引的分片数量在创建时就确定了,后续不可更改。
- 分片分为两种类型:
- 主分片:索引创建时定义的数量,所有文档的索引和存储操作首先在主分片上进行,一个索引的文档只能存储在它的主分片上。
- 副本分片:主分片的拷贝,副本分片的主要作用有两个:
- 高可用:当主分片所在的节点宕机时,副本分片可以被提升为新的主分片,保证服务不中断。
- 扩展查询性能:读请求可以由主分片或副本分片共同分担,从而提高查询的吞吐量。
-
副本
- 副本是主分片的精确拷贝。
- 副本的数量可以在索引创建后随时动态调整。
- 数据写入流程:数据先写入主分片,然后同步到所有副本分片,当所有副本分片都同步成功后,写入操作才算成功。
- 数据读取流程:读请求可以路由到主分片或任何一个副本分片,这由协调节点根据负载情况决定。
分片与副本的示例:
假设我们创建一个名为 logs 的索引,设置 number_of_shards=3,number_of_replicas=1。
- 这意味着这个索引被分为 3 个主分片(P0, P1, P2)。
- 每个主分片都会有 1 个副本分片(R0, R1, R2)。
- 这 6 个分片(3 主 + 3 副)会分布在集群的不同数据节点上。
工作原理:写入与查询流程
理解了分片和副本后,我们来看数据是如何在集群中流动的。
数据写入流程
当客户端向一个协调节点发送一个索引(写入)请求时:
- 路由:协调节点根据文档的
_id或路由值,计算出该文档应该被存储在哪个主分片上,计算公式通常是shard = hash(_routing) % number_of_primary_shards,这确保了同一文档总是被路由到同一个主分片。 - 转发:协调节点将请求转发到目标主分片所在的节点。
- 处理:主分片接收到请求后,先将数据写入内存缓存和 Translog(事务日志,用于数据恢复),然后执行索引操作,将数据写入 Lucene 索引文件。
- 副本同步:主分片成功索引数据后,会将操作并行转发给其所有的副本分片,副本分片执行同样的操作,将数据同步到自己的 Lucene 索引中。
- 确认:当所有副本分片都同步完成后,主分片会向协调节点发送成功响应。
- 完成:协调节点收到主分片的响应后,向客户端返回成功确认。
这个过程保证了数据在写入时的一致性(至少写入成功一份,即主分片)。
数据查询流程
当客户端向一个协调节点发送一个搜索请求时:
- 广播:协调节点将查询请求广播到索引的所有主分片和副本分片上(即所有活动的分片)。
- 本地查询:每个分片在本地执行查询,根据查询条件从自己的倒排索引中找出匹配的文档,并生成一个包含文档 ID 和
