当前位置: 首页 > news >正文

【Elasticsearch从入门到精通】第12篇:Elasticsearch读写原理——主备复制模型与数据一致性

上一篇【第11篇】Elasticsearch索引API详解
下一篇【第13篇】Elasticsearch索引API深度解析


摘要

Elasticsearch作为分布式搜索引擎,其读写原理是理解整个系统行为的基石。本文从Primary-Backup主备复制模型切入,详细拆解一次写入请求从协调节点路由primary分片本地执行再到replica分片同步确认的完整链路,同时覆盖读请求的分片选择策略结果合并排序机制。文章重点解析consistency参数中one、quorum、all三种级别的一致性保障差异,以及primary故障与replica故障场景下的容错处理逻辑。通过一次线上写入失败的完整排查过程,将理论落到实践,帮助读者真正掌握ES读写行为的调试与优化方法。


一、数据复制模型:Primary-Backup设计

1.1 为什么需要复制

Elasticsearch运行在分布式集群中,单台机器随时可能宕机。如果一份数据只存一个副本,节点故障就意味着数据丢失。正因为此,ES为每个分片引入了主备复制模型(Primary-Backup Replication):每个索引被切分为多个主分片(Primary Shard),每个主分片可配置0个或多个副本分片(Replica Shard)。

索引 "products":3个primary分片,1个replica副本 ┌─────────────────────────────────────────────────┐ │ 分片 P0 (primary) 分片 R0 (replica) │ │ 分片 P1 (primary) 分片 R1 (replica) │ │ 分片 P2 (primary) 分片 R2 (replica) │ └─────────────────────────────────────────────────┘

关键规则

  • 主分片数量在索引创建时确定,后续不可修改
  • 副本分片数量可随时动态调整
  • Primary及其对应的Replica绝不能在同一节点上
  • 写操作只能由Primary分片处理,副本只负责复制
  • 读操作可以由Primary或任意Replica处理

1.2 Primary和Replica的角色分工

角色写入读取故障转移
Primary分片唯一写入入口,索引文档可参与读取故障时Replica晋升为Primary
Replica分片被动接收Primary的复制数据可参与读取(与Primary等价)故障时仅减少可用副本数

这个模型的设计哲学很简单:写走Primary保证数据一致性,读走所有分片实现负载均衡


二、基本写模型流程

2.1 写请求的完整链路

一次文档写入的完整流程涉及三个角色:协调节点(Coordinating Node)Primary分片所在节点Replica分片所在节点

客户端 → 协调节点 → Primary分片 → Replica分片 → 协调节点 → 客户端

详细步骤

  1. 客户端发送写请求到集群中的任意节点(该节点自动成为协调节点)
  2. 协调节点计算路由:根据文档ID通过哈希算法确定目标分片shard = hash(_id) % number_of_primary_shards
  3. 协调节点转发请求到Primary分片所在的节点
  4. Primary分片执行写入:验证文档、解析字段、写入Lucene索引
  5. Primary将操作转发给所有In-Sync Replica分片
  6. Replica分片执行相同操作并确认成功
  7. Primary收集确认:当满足consistency要求数量的Replica确认后,返回成功给协调节点
  8. 协调节点返回结果给客户端

2.2 路由计算机制

文档写入哪个分片,由路由公式决定:

shard_num = hash(_routing) % num_primary_shards

默认情况下,_routing等于文档的_id。你也可以通过routing参数指定自定义路由值,确保相关联的文档落在同一个分片上。

POST/products/_doc?routing=user_123{"name":"无线蓝牙耳机","price":299,"category":"电子产品"}

2.3 刷新与刷盘机制

写入操作并不会立即可见。ES在内存中维护一个索引缓冲区(Indexing Buffer),文档先写入缓冲区,然后经历两个关键操作:

操作触发条件作用性能影响
Refresh(刷新)默认每1秒自动执行将缓冲区数据生成新的Lucene段(Segment),使文档可搜索轻量,频繁执行影响不大
Flush(刷盘)translog达到阈值或每30分钟将内存中的段通过fsync持久化到磁盘,清空translog较重,不应频繁执行

刷新频率可通过index.refresh_interval设置:

PUT/products/_settings{"index":{"refresh_interval":"5s"}}

关键认知:文档写入后,默认最多1秒后即可搜索到(NRT,Near Real-Time,近实时搜索)。如果需要写入后立即可见,可以在写入请求上加refresh=wait_for参数:

POST/products/_doc?refresh=wait_for{"name":"机械键盘","price":599}

2.4 Translog事务日志

为防止节点宕机丢失内存中的写入数据,ES引入了Translog(事务日志)

  • 每次写入操作先追加到Translog(顺序写,性能极高)
  • 写入成功后,数据才写入内存缓冲区
  • 节点重启时,通过重放Translog恢复未持久化的数据

这与数据库的WAL(Write-Ahead Log)机制一致——先记日志,再改数据


三、写流程错误处理

3.1 Primary分片故障

当Primary分片所在的节点宕机时,ES的故障转移流程如下:

  1. Master节点检测到Primary节点失联(默认30秒内无心跳)
  2. Master从In-Sync Replica中选举新的Primary
  3. 集群元数据更新,新Primary接管写入职责
  4. 协调节点重新路由请求到新Primary

整个过程中可能会有短暂的写入失败,客户端需要做好重试处理。重建索引时指定wait_for_active_shards可以控制最小存活分片数:

PUT/products{"settings":{"index":{"number_of_shards":3,"number_of_replicas":2}}}

3.2 Replica分片故障

Replica故障的处理相对简单:

  • Primary继续处理写入,将操作记录在Translog中
  • Master将故障Replica标记为Out-of-Sync
  • 当Replica节点恢复后,Master通知Primary将差异数据同步过去
  • 如果故障时间过长,需要从Primary做全量拷贝

3.3 写入失败的常见原因与排查

故障类型现象排查手段
分片分配失败索引状态为Yellow或RedGET _cat/shards?v查看未分配原因
磁盘空间不足写入返回403 ForbiddenGET _cat/allocation?v查看磁盘使用率
版本冲突返回409 VersionConflict检查并发写入场景,考虑使用version_type=external
映射冲突字段类型不匹配GET /index/_mapping检查字段定义
协调节点超时写入hang住后超时检查GC日志和线程池队列深度

四、基本读模型

4.1 读请求的完整链路

读请求的流程与写请求类似,但多了一步结果合并

  1. 客户端发送查询请求到任意节点(协调节点)
  2. 协调节点确定目标分片:根据查询范围选择需要参与的分片
  3. 协调节点分发查询到各分片(优先本地分片,减少网络开销)
  4. 每个分片独立执行查询,返回Top N结果给协调节点
  5. 协调节点合并结果:对来自各分片的结果进行全局排序、去重
  6. 协调节点返回最终结果给客户端

4.2 分片选择策略

对于读请求,ES支持多种分片选择策略,由preference参数控制:

GET/products/_search?preference=_local{"query":{"match":{"name":"耳机"}}}
preference值行为适用场景
_local优先使用本地分片,减少网络开销对实时性要求不高的查询
_primary只查询Primary分片需要最新写入数据的场景
_replica只查询Replica分片降低Primary负载
_primary_first先Primary,不可用时用Replica偏向一致性的选择
_replica_first先Replica,不可用时用Primary偏向性能的选择
_only_local只查询本地分片极限降低延迟
自定义字符串相同字符串落到相同分片翻页一致性和缓存利用

默认行为:ES使用自适应副本选择(Adaptive Replica Selection,ARS),根据各分片的响应时间、队列长度、服务时间等指标动态选择最快的副本,而非简单的round-robin轮询。

4.3 结果整合与排序

协调节点需要合并来自多个分片的结果,这一过程称为两阶段查询

  • Query阶段:协调节点向各分片发送查询,每个分片返回from + size个文档的排序值和ID
  • Fetch阶段:协调节点根据排序结果,向包含这些文档的分片请求完整文档内容

这就是search_type默认为query_then_fetch的原因——先查后取。


五、一致性保证机制

5.1 Consistency参数详解

ES通过consistency参数(7.x版本后迁移为wait_for_active_shards)控制写入时所需的最小确认分片数。

级别含义最小确认数适用场景
one只要Primary写入成功即返回1个分片可容忍数据丢失的日志场景
quorum(默认)需要多数分片确认(replicas + 1) / 2 + 1个分片通用业务场景
all所有分片都需确认replicas + 1个分片金融、交易等强一致性场景

5.2 Quorum的计算

假设有1个Primary + 2个Replica,共3个分片,则quorum为:

quorum = int((primary + replicas) / 2) + 1 = int((1 + 2) / 2) + 1 = int(1.5) + 1 = 2

即至少需要2个分片确认(可以是Primary + 1个Replica)。如果只有2个分片存活(比如1个Replica故障),quorum仍然可以满足,写入不受影响。

在新版ES中,使用wait_for_active_shards参数:

PUT/orders/_doc/1001?wait_for_active_shards=2{"product":"笔记本电脑","price":6999,"status":"paid"}

5.3 版本控制与乐观锁

ES使用_version字段实现乐观锁,防止并发写入冲突:

PUT/products/_doc/abc123?version=5{"name":"蓝牙耳机升级版","price":359}

如果当前文档版本不是5,请求会返回409冲突错误。这是分布式系统中实现数据一致性的经典手段。

版本控制方式说明适用场景
Internal VersioningES自动维护的_version字段,从1递增默认版本控制
External Versioning业务系统传入外部版本号与外部系统保持版本同步
If-Seq-No / If-Primary-Trem基于序列号和任期号的条件更新替代_version的现代方式

使用if_seq_noif_primary_term进行条件更新:

PUT/products/_doc/abc123?if_seq_no=10&if_primary_term=1{"name":"蓝牙耳机终极版","price":399}

六、实战案例:写入失败完整排查过程

6.1 故障场景

某电商系统反馈:商品信息批量导入时,部分文档写入失败,返回503 Service Unavailable或超时错误。环境配置:

  • 集群:3个节点
  • 索引products:3 Primary + 1 Replica
  • 并发写入:5个线程,每批1000条

6.2 排查步骤

第一步:检查集群健康状态

GET_cluster/health

响应:

{"cluster_name":"ecommerce","status":"yellow","unassigned_shards":2,"number_of_nodes":3,"number_of_data_nodes":3,"active_primary_shards":3,"active_shards":4,"relocating_shards":0,"initializing_shards":0}

发现集群状态为Yellow,有2个分片未分配。根据CAP理论,此时可能某些副本不可用。

第二步:查看未分配分片详情

GET_cat/shards/products?v&h=index,shard,prirep,state,unassigned.reason,node

输出显示有2个replica分片处于UNASSIGNED状态,原因是NODE_LEFT(节点离开集群)。

第三步:检查节点和磁盘状态

GET_cat/allocation?v

发现其中一个数据节点磁盘使用率达到95%,触发了ES的磁盘水位线保护机制——默认当磁盘使用超过95%时,ES会将对应节点的分片迁移出去,拒绝新分片的分配。这导致replica分片无法分配。

第四步:检查写入失败的详细日志

GET/_cluster/allocation/explain{"index":"products","shard":0,"primary":false}

响应中explanation字段提示:the node is above the high watermark cluster setting,确认是磁盘水位线问题。

6.3 解决方案

清理磁盘空间或调整水位线设置(临时方案)

PUT_cluster/settings{"transient":{"cluster.routing.allocation.disk.watermark.low":"90%","cluster.routing.allocation.disk.watermark.high":"95%","cluster.routing.allocation.disk.watermark.flood_stage":"98%"}}

清理后触发分片重新分配

POST_cluster/reroute?retry_failed=true

分片分配完成后,集群恢复Green状态,写入恢复正常。

6.4 根因总结

层级问题原因解决
表现层写入返回503/超时wait_for_active_shards默认all,找不到足够分片先恢复集群健康
中间层分片未分配磁盘水位线触发保护清理磁盘或扩容
根因层磁盘使用率过高日志未及时清理、索引未设置生命周期配置ILM策略自动清理

七、最佳实践总结

7.1 写入优化

  1. 批量写入优先使用_bulkAPI,减少网络往返次数
  2. 合理设置refresh_interval:高写入场景设为30s甚至-1(关闭自动刷新),写入完成后再恢复
  3. 关闭不需要的实时性:批处理导入时设置refresh_interval=-1number_of_replicas=0,完成后恢复
  4. 使用自动生成的ID:ES自行生成ID比客户端指定ID性能更好(避免额外的查找开销)
  5. 监控线程池队列:关注writesearch线程池的拒绝情况

7.2 一致性配置

  1. 默认使用quorum:平衡性能和一致性
  2. 日志分析场景用one:允许少量数据丢失,换取更高写入吞吐
  3. 金融交易场景用all:虽然影响性能,但数据完整性优先
  4. 生产环境number_of_replicas >= 1:至少保留一个副本保证容灾

7.3 集群运维

  1. 设置磁盘水位线告警:磁盘使用率80%就该关注
  2. 配置ILM生命周期策略:自动清理过期索引
  3. 定期检查集群健康GET _cluster/health纳入监控
  4. 写入失败时先看集群状态:Yellow/Red状态是多数写入问题的根源

7.4 读请求优化

  1. 根据场景选择preference:需要最新数据用_primary,追求性能用_local
  2. 合理设置size:不要无意义地请求超大结果集
  3. 使用filter_path减少传输量:只返回需要的字段
GET/products/_search?filter_path=hits.hits._source.name,hits.hits._source.price{"query":{"match":{"name":"耳机"}}}

八、小结

Elasticsearch的读写原理并不神秘。写请求遵循协调节点→Primary分片→Replica分片的单向链路,由Primary统一管理写入以确保一致性;读请求则通过协调节点的两阶段查询(Query Then Fetch),从多个分片并行获取结果并合并排序。理解这套机制,你就能在遇到性能瓶颈或写入故障时快速定位问题——是Primary分片负载过高、是副本同步延迟、还是磁盘水位线触发了保护机制——都能在分片级别的视角下找到答案。

下一篇文章我们将深入Elasticsearch的索引API,探讨索引的生命周期管理和映射策略。


上一篇【第11篇】Elasticsearch索引API详解
下一篇【第13篇】Elasticsearch索引API深度解析


http://www.jsqmd.com/news/874909/

相关文章:

  • Bittensor:去中心化AI网络的架构、挑战与激励模型优化
  • 实战指南:用Python和PyTorch一步步搭建TFT模型,搞定电力负荷多步预测
  • 高维非线性数据下的偏均值独立性检验:原理、实现与应用
  • 量子计算在组合优化与蛋白质折叠中的应用
  • 统信UOS/麒麟KYLINOS用户看过来:除了Termius,这款开源免费的SSH工具electerm更香吗?
  • 【Elasticsearch从入门到精通】第13篇:Elasticsearch索引API深度解析——自动创建、路由与并发控制
  • 基尔代尔 才是天才吗
  • 告别踩坑:手把手教你为openEuler 22.03 LST配置RealVNC 6.11远程桌面(含序列号激活)
  • STR91xFA Rev H内存验证错误解决方案
  • # 软考软件设计师 · 考前3天终极实战全攻略
  • 量子电路生成式AI技术:原理、应用与挑战
  • 嵌入式GPU如何实现边缘视觉应用820%性能跃迁:从架构解析到实战优化
  • XRDP远程桌面太卡?手把手教你优化Ubuntu 22.04的传输性能与画质
  • 告别K-means!用DBSCAN搞定雷达点云聚类,手把手教你调参(附Matlab代码)
  • Cortex-M55缓存维护与SAU重映射安全实践
  • dos系统时代
  • AI与PDCA循环融合:构建韧性医院物流系统的实践指南
  • 手把手教你用udev规则在统信UOS上灵活管控USB设备(允许特定U盘/完全禁用)
  • 2026年4月螺母供应商口碑分析,字槽伞头螺丝/螺母/双牙长方型T帽/字槽圆头自攻尖尾螺钉,螺母厂家口碑推荐 - 品牌推荐师
  • openKylin双系统安装保姆级复盘:我踩过的三个坑(分区、引导、驱动)及完美解决方案
  • 从‘封建网络’到‘选项框架’:手把手拆解5种主流HRL算法核心思想与PyTorch实现要点
  • 深入Linux内核:fixed-link如何用软件模拟一个PHY,并接入MDIO总线框架
  • MacBook新手别慌!Final Cut Pro 10.6.5保姆级教程:从导入素材到导出网课视频全流程
  • # 软考软件设计师 · 考前2天轻松复习与终极必背手册
  • Spark Transformer:稀疏激活技术提升大模型计算效率
  • 【2026年阿里巴巴集团暑期实习- 5月23日-算法岗-第一题- 荆棘林的最优砍断计划】(题目+思路+JavaC++Python解析+在线测试)
  • 卫星遥感与AI融合的海洋监测技术解析
  • Linux下离线安装Mamba_SSM和Causal-Conv1d避坑指南(附CUDA 11.8 + PyTorch 2.0环境包)
  • 避坑指南:ARM架构麒麟V10 SP2安装telnet时,如何解决‘依赖地狱’和版本匹配问题
  • AI司法应用中的算法公平性:从数据偏见到保护属性选择的技术实践