在构建企业级搜索平台时,我们不仅关注“快”,更关注“准”和“稳”。Easysearch 作为一个分布式的 Near Real-Time(近实时)搜索引擎,其核心架构设计解决的最根本问题是:在不可靠的网络和硬件之上,构建一个可靠的数据系统。
本文将深入剖析 Easysearch 分布式架构的三大基石:集群协调模型、数据复制模型以及并发控制机制。
一、 集群协调:去中心化与共识算法 #
Easysearch 采用的是 P2P(Peer-to-Peer)网络架构,但在元数据管理上采用了 主节点(Master) 负责制的混合模式。这种设计既保证了数据传输的高吞吐,又确保了集群状态的一致性。
1. 选主与脑裂防护(Zen Discovery) #
Easysearch 内部运行着一套名为 Zen Discovery(在较新版本中演进为基于 Quorum 的协调层)的模块,负责节点发现和 Master 选举。
- 选举机制:集群启动时,各个
master-eligible节点会相互“Ping”。如果发现没有 Master,它们会根据 Node ID 和配置权重进行投票。只有获得 法定票数(Quorum) 的节点才能当选 Master。 - 脑裂(Split-Brain)防护:
- 在分布式系统中,网络分区可能导致集群分裂成两个子集群,各自选出 Master,导致数据分叉。
- Easysearch 严格遵循
minimum_master_nodes(或新版的自动引导机制)原则,要求N/2 + 1个节点存活才能进行元数据变更。这从算法层面杜绝了脑裂的发生。
2. Cluster State 的原子性广播 #
Cluster State 是集群的“唯一真理”,包含 Mapping、Settings、路由表等。为了在千节点规模下高效同步状态,Easysearch 做了如下设计:
- Diff(增量)同步:Master 节点不会每次都广播几 MB 的全量 Cluster State。它会计算当前版本与上一版本的差异(Delta),仅将几 KB 的 Diff 数据发送给其他节点。
- 两阶段提交(2PC):
- Publish:Master 将状态变更发送给所有节点。
- Commit:当 Master 收到半数以上节点的 Ack 后,发送 Commit 指令,所有节点同时应用变更。
二、 数据模型:分片、路由与不可变性 #
1. 分片(Shard)即 Lucene 索引 #
Easysearch 的一个 Shard 本质上就是一个 Apache Lucene 索引实例。
- 资源隔离:每个 Shard 是一个独立的搜索引擎,拥有独立的倒排索引、词典和缓存。
- 限制:由于 Lucene 的设计,一个 Shard 内的文档数有上限(约 21 亿,
Integer.MAX_VALUE),且 Shard 越多,元数据开销越大。因此合理的分片规划(如单分片 30GB-50GB)是性能调优的关键。
2. 确定性路由算法 #
文档存放在哪个分片,由以下公式决定:shard = hash(routing) % number_of_primary_shards
- routing:默认是文档 ID,也可以自定义(如按 UserID 路由)。
- 不可变性:这就是为什么索引创建后,主分片数量不能修改的原因。一旦修改,取模公式的结果就会改变,已存的数据就“找不到了”。(注:Easysearch 支持 Split API 裂变分片,但本质是重新创建索引)。
三、 数据复制与一致性模型(Replication & Consistency) #
这是分布式数据库最核心的部分。Easysearch 采用 Primary-Backup(主备) 复制模型。
1. 写入一致性流程 #
不同于 Cassandra 的最终一致性,Easysearch 倾向于保证强一致性(或顺序一致性):
- Request:请求到达协调节点。
- Primary:请求转发到主分片。主分片验证请求,执行写入,并生成 Sequence ID。
- Replicas:主分片并发地将操作转发给所有副本分片(In-sync Replicas)。
- Ack:一旦所有活跃的副本都执行成功,主分片向协调节点确认,协调节点向客户端返回成功。
2. Sequence IDs 与快速恢复 #
早期的 ES 依赖版本号(Version)来做并发控制,但在故障恢复时效率极低。Easysearch 引入了更先进的 Sequence IDs 机制:
- _seq_no:每个操作(Index, Delete)在分片级别都会被分配一个严格递增的序列号。
- _primary_term:主分片的任期号。每次主分片发生切换(Failover),Term + 1。
- Global/Local Checkpoint:
- 每个分片维护一个“检查点”,表示“在此序列号之前的所有操作都已持久化且一致”。
- 快速恢复(Peer Recovery):当一个副本节点离线重启后,它不需要从主分片拷贝全量数据。它只需对比 Checkpoint,向主分片请求 重放(Replay) 丢失的那一小段操作(Operations)。这使得 TB 级数据的恢复时间从小时级缩短到秒级。
四、 并发控制(Concurrency Control) #
在分布式高并发写入场景下,如何防止“旧数据覆盖新数据”?Easysearch 提供了完善的乐观锁机制。
1. 乐观并发控制(OCC) #
Easysearch 不使用悲观锁(会阻塞性能),而是利用元数据进行检测:
- 基于 Version:
PUT /index/_doc/1?version=5。如果当前版本不是 5,报错。 - 基于 Sequence ID(推荐):
PUT /index/_doc/1?if_seq_no=1023&if_primary_term=2
这是 CAS(Compare-And-Swap)操作的基础。它确保了只有在你知道数据的当前状态时,才能修改它。
五、 数据持久性(Durability)与 Translog #
内存写入虽然快,但掉电即失。Easysearch 通过 Translog(事务日志) 保证数据不丢。
1. Write Ahead Log (WAL) 机制 #
- 每次写入请求,除了写入内存 Buffer(用于生成可搜索的 Segment),还会顺序追加到磁盘上的 Translog 文件。
- Fsync 策略:
index.translog.durability: request(默认):每次请求都强制刷盘。最安全,但制约 IOPS。index.translog.durability: async:异步刷盘(默认 5秒)。性能极高,但风险是节点宕机时可能丢失最近 5秒的数据。
2. 崩溃恢复 #
重启时,Easysearch 会重放 Translog 中的操作,将那些还在内存 Buffer 中未落盘的数据恢复回来。
六、 总结 #
Easysearch 的分布式架构设计,是在性能、一致性和可用性之间寻找完美的平衡点:
- Cluster State Diff 解决了大规模集群的元数据同步风暴问题。
- Sequence IDs + Checkpoints 解决了分布式环境下的数据一致性和快速恢复问题。
- Translog + Segment 的组合,既实现了近实时搜索,又保证了数据的持久性。
理解这些核心设计,有助于我们在生产环境中更好地进行容量规划、性能调优和故障排查,充分释放 Easysearch 作为企业级搜索底座的潜力。





