ES的分布式架构原理
点击勘误issues (opens new window),哪吒感谢大家的阅读
# ES的分布式架构原理
Elasticsearch(ES)是一种基于 分布式架构 的搜索和分析引擎。它的分布式架构原理可以用“图书馆分区管理”的类比来形象解释,帮助理解其核心概念。
核心组件
- 索引(Index):像图书馆的书架,用于存储同类型的数据。
例如,一个包含用户信息的索引可以类比成专门放“用户数据”的书架。
- 文档(Document):索引中的最小单位,类似于书架上的一本书。
每个用户的数据(如姓名、年龄、地址)是一份“文档”。
- 分片(Shard):每个索引被分成的小块,就像图书馆将书架上的书分区域存放,方便快速查找。
一个索引可能很大,为了管理效率,会将它拆分成多个分片。
- 副本(Replica):分片的备份,确保数据安全和高可用。
图书馆的每本重要书籍会有多份副本,分别放在不同的分区。
- 节点(Node):负责存储数据和处理请求的服务器,类似于图书馆的工作人员。
每个工作人员管理一部分书架,并协同其他工作人员完成借书或查询的任务。
- 集群(Cluster):多个节点组成的整体,类似于一个大型图书馆的管理系统。
一个集群中的节点协作存储和查询数据,就像所有工作人员一起为读者服务。
分布式架构工作原理
数据分片存储
当我们向 ES 添加数据时,它会根据索引的分片策略,将文档分配到不同的分片。 比如,图书馆有一个存储用户数据的书架(索引),这本书(文档)会按照某种规则被分配到某个区域(分片)。
分片分布到不同节点
每个分片存储在集群的不同节点上,确保数据分散存储,提高查询效率。 类比:图书馆的不同区域(分片)由不同的工作人员(节点)管理。
副本提高可靠性
每个分片都有一到多个副本,存储在不同节点上,确保即使某个节点故障,数据依然可用。 类比:重要书籍的多份副本存放在不同的图书馆分区,即使某个分区损坏,也可以从其他分区取书。
查询时多节点协作
当用户发起搜索请求,ES 会将请求转发到负责相关分片的节点,多个节点协作完成查询并返回结果。 类比:读者提出“查询某个用户数据”,工作人员会协作查找分布在多个分区的书籍,并合并结果后返回给读者。
关键特性
水平扩展
如果图书馆有更多书籍(数据增长),可以增加更多分区(节点)来存放书籍,且不会影响查询性能。 在 ES 中,集群可以动态添加节点,分片会被自动重新分配,确保数据均匀分布。
高可用性
如果某个节点(工作人员)失效,系统可以通过其他节点上的副本恢复数据,确保服务不中断。 类比:如果某个分区的工作人员不在,其他分区有备份的书籍,也可以完成任务。
快速查询
ES 会为每个文档建立倒排索引,类似于图书馆为书籍建立的索引卡,方便快速查找特定内容。
分布式写入和负载均衡
数据写入会被均匀分布到不同的节点,避免单个节点负载过高。 类比:图书馆让多个工作人员协作完成书籍的分类和上架任务。
# 示例:分布式架构下的搜索
假设图书馆有 3 个分区,负责管理一本书籍索引。每个分区分别存储了一部分书籍。
书籍分布:
- 分区 A 存储用户 A-D 的数据。
- 分区 B 存储用户 E-H 的数据。
- 分区 C 存储用户 I-L 的数据。
搜索流程:
- 读者想查询用户“G”的信息。
- 图书馆系统会把请求发给分区 B 的工作人员,因为用户 G 的数据存储在 B。
- 分区 B 查找到数据并返回给读者。
如果分区 B 故障:
- 系统会自动从分区 C 的副本中查询用户 G 的数据,确保服务不中断。
# 总结
ES 的分布式架构以 分片和副本 为核心:
- 分片:将数据拆分存储,提升性能。
- 副本:数据冗余存储,确保高可用。
- 节点:分布式存储和处理,支持动态扩展。
在搜索这块,lucene 是最流行的搜索库。几年前业内一般都问,你了解 lucene 吗?你知道倒排索引的原理吗?现在早已经 out 了,因为现在很多项目都是直接用基于 lucene 的分布式搜索引擎—— ElasticSearch,简称为 ES。
而现在分布式搜索基本已经成为大部分互联网行业的 Java 系统的标配,其中尤为流行的就是 ES,前几年 ES 没火的时候,大家一般用 solr。但是这两年基本大部分企业和项目都开始转向 ES 了。
# ES 的分布式架构设计
ElasticSearch 设计的理念就是分布式搜索引擎,底层其实还是基于 lucene 的。核心思想就是在多台机器上启动多个 ES 进程实例,组成了一个 ES 集群。
ES 中存储数据的基本单位是索引,比如说你现在要在 ES 中存储一些订单数据,你就应该在 ES 中创建一个索引 order_idx ,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是 mysql 里的一个数据库。
index -> type -> mapping -> document -> field
这样吧,为了做个更直白的介绍,我在这里做个类比。但是切记,不要划等号,类比只是为了便于理解。
index 相当于 mysql 数据库。而 type 没法跟 mysql 里去对比,一个 index 里可以有多个 type,每个 type 的字段都是差不多的,但是有一些略微的差别。假设有一个 index,是订单 index,里面专门是放订单数据的。就好比说你在 mysql 中建表,有些订单是实物商品的订单,比如一件衣服、一双鞋子;有些订单是虚拟商品的订单,比如游戏点卡,话费充值。就两种订单大部分字段是一样的,但是少部分字段可能有略微的一些差别。
所以就会在订单 index 里,建两个 type,一个是实物商品订单 type,一个是虚拟商品订单 type,这两个 type 大部分字段是一样的,少部分字段是不一样的。
很多情况下,一个 index 里可能就一个 type,但是确实如果说是一个 index 里有多个 type 的情况(注意, mapping types 这个概念在 ElasticSearch 7. X 已被完全移除,详细说明可以参考官方文档),你可以认为 index 是一个类别的表,具体的每个 type 代表了 mysql 中的一个表。每个 type 有一个 mapping,如果你认为一个 type 是具体的一个表,index 就代表多个 type 同属于的一个类型,而 mapping 就是这个 type 的表结构定义,你在 mysql 中创建一个表,肯定是要定义表结构的,里面有哪些字段,每个字段是什么类型。实际上你往 index 里的一个 type 里面写的一条数据,叫做一条 document,一条 document 就代表了 mysql 中某个表里的一行,每个 document 有多个 field,每个 field 就代表了这个 document 中的一个字段的值。
你搞一个索引,这个索引可以拆分成多个 shard ,每个 shard 存储部分数据。拆分多个 shard 是有好处的,一是支持横向扩展,比如你数据量是 3T,3 个 shard,每个 shard 就 1T 的数据,若现在数据量增加到 4T,怎么扩展,很简单,重新建一个有 4 个 shard 的索引,将数据导进去;二是提高性能,数据分布在多个 shard,即多台服务器上,所有的操作,都会在多台机器上并行分布式执行,提高了吞吐量和性能。
接着就是这个 shard 的数据实际是有多个备份,就是说每个 shard 都有一个 primary shard ,负责写入数据,但是还有几个 replica shard 。 primary shard 写入数据之后,会将数据同步到其他几个 replica shard 上去。
通过这个 replica 的方案,每个 shard 的数据都有多个备份,如果某个机器宕机了,没关系啊,还有别的数据副本在别的机器上呢。高可用了吧。
ES 集群多个节点,会自动选举一个节点为 master 节点,这个 master 节点其实就是干一些管理的工作的,比如维护索引元数据、负责切换 primary shard 和 replica shard 身份等。要是 master 节点宕机了,那么会重新选举一个节点为 master 节点。
如果是非 master 节点宕机了,那么会由 master 节点,让那个宕机节点上的 primary shard 的身份转移到其他机器上的 replica shard。接着你要是修复了那个宕机机器,重启了之后,master 节点会控制将缺失的 replica shard 分配过去,同步后续修改的数据之类的,让集群恢复正常。
说得更简单一点,就是说如果某个非 master 节点宕机了。那么此节点上的 primary shard 不就没了。那好,master 会让 primary shard 对应的 replica shard(在其他机器上)切换为 primary shard。如果宕机的机器修复了,修复后的节点也不再是 primary shard,而是 replica shard。