惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
Cloudbric
Cloudbric
C
CERT Recently Published Vulnerability Notes
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
C
Cisco Blogs
T
Tenable Blog
P
Privacy International News Feed
T
The Exploit Database - CXSecurity.com
I
Intezer
AWS News Blog
AWS News Blog
IT之家
IT之家
博客园 - 司徒正美
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Spread Privacy
Spread Privacy
S
SegmentFault 最新的问题
博客园 - Franky
人人都是产品经理
人人都是产品经理
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
Visual Studio Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
Latest news
Latest news
Scott Helme
Scott Helme
腾讯CDC
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
A
Arctic Wolf
S
Securelist
雷峰网
雷峰网
The GitHub Blog
The GitHub Blog
Project Zero
Project Zero
Google DeepMind News
Google DeepMind News
P
Palo Alto Networks Blog
F
Fortinet All Blogs
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
Security Archives - TechRepublic
Security Archives - TechRepublic
The Last Watchdog
The Last Watchdog
WordPress大学
WordPress大学
MongoDB | Blog
MongoDB | Blog
L
LINUX DO - 最新话题
S
Schneier on Security
NISL@THU
NISL@THU
Jina AI
Jina AI
M
MIT News - Artificial intelligence

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 分享一下笔者的 Mac 装机必备软件 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
ElasticSearch主分片和副本分片概念详解
huangSir-devops · 2026-04-17 · via 博客园_首页

主分片概述

ES 主分片的本质是独立的 Lucene 实例,主分片负责数据的写入、修改、删除等操作,可以理解成凡是写入操作一定会走主分片。
ES中有一个文档(doc)的概念,每一个文档只会属于一个主分片.

例如一个ES索引有三个主分片分别在A、B、C三个集群节点上,当一个user的doc需要写入时,那么这个user的doc只会在其中一个主分片上,而不是在每个集群节点上都有。

主分片数量在ES索引创建时指定,当索引创建完成之后,则不允许再修改主分片的数量,例如下面的案例:

# 创建一个索引,指定主分片数量为3
PUT /test01
{
  "settings": {
    "number_of_shards": 3
  }
}

# 然后再尝试修改其主分片数量
PUT /test01/_settings
{
  "settings": {
    "number_of_shards": 3
  }
}

# 报错如下:
{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "Can't update non dynamic settings [[index.number_of_shards]] for open indices [[test01/FPlsR0XQQba41Fr_2_8m7g]]"
      }
    ],
    "type" : "illegal_argument_exception",
    "reason" : "Can't update non dynamic settings [[index.number_of_shards]] for open indices [[test01/FPlsR0XQQba41Fr_2_8m7g]]"
  },
  "status" : 400
}

当我们指定主分片数量时,主分片数量并不是越多越好,在不考虑机器性能的情况下,主分片数量一般是根据其ES集群节点数量及索引存储的数据量来决定,有几个参考值:

  • 单个主分片大小控制在 20GB ~ 50GB 范围内,性能最优。
  • 主分片数量一般是集群节点的1-3倍。例如3节点的集群,一个索引的主分片数量可以为3/6/9。

当然这些仅仅只是参考,例如一个索引的存储的数据量仅仅只有几个G,那么此时只需要给一个主分片即可。

为什么呢?
上文说过 ES 主分片的本质是独立的 Lucene 实例,每个分片都要占用独立的系统资源、元数据开销、调度开销,数据量小的时候多分片的弊端会被无限放大,收益为负,具体体现在5个维度:

1、堆内存资源严重浪费(最直观的影响),每个 Lucene 实例都要占用固定的堆内存来存储:索引元数据(段信息、倒排词典、字段缓存、过滤器缓存等),写入/查询线程上下文、连接资源,分片级别的监控、统计数据。在小分片场景下,这些固定开销的占比会极高。
例如:1GB 索引用 1 个分片,元数据开销约 10~30MB,占比 1%~3%,而1GB 索引用 10 个分片,元数据总开销约 100~300MB,占比 10%~30%

2、查询性能不升反降(最影响业务的问题)
ES 查询的标准流程是:协调节点接收请求 → 路由到所有相关分片 → 每个分片独立执行查询 → 协调节点聚合所有分片的结果 → 返回给用户。
当主分片越多,调度开销+聚合开销越大。
比如 1GB 索引用 1 个分片:只需要执行1次查询,无需聚合,延迟 10ms 以内
比如 1GB 索引用 10 个分片:需要执行10次查询,还要合并10份结果,延迟会变成 30~50ms,是单分片的 3~5 倍
高并发场景下还会放大问题:协调节点要同时处理大量分片级别的请求,很容易出现CPU占满、OOM等问题。

3、集群元数据管理压力指数级上升(影响集群稳定性)
ES Master 节点需要管理全集群所有分片的元数据,分片越多,会导致集群状态(Cluster State)体积越大,Master 同步元数据到所有节点的耗时越长,创建/删除索引、分片分配、节点上下线等集群操作的执行速度越慢,极端情况会导致 Master 卡顿、集群无响应,元数据写入持久化的频率越高,Master 磁盘IO压力越大。
生产经验:总分片数超过 1 万的集群,稳定性会明显下降,很多操作都会变慢。

4、写入性能不会提升反而下降
很多人误以为「分片越多写入越快」,但小数据量场景下完全相反,当写入时需要计算路由决定写到哪个分片,分片越多路由开销越大。每个分片都要独立执行段合并、刷新磁盘等操作,小分片会产生大量小的段文件,合并开销翻倍。单分片写入性能本身就可以达到 5000~20000 TPS,小数据量(日写入低于 100GB)完全不需要多分片来扛写入压力。

5、运维复杂度大幅提升
分片分配、迁移、恢复的成本更高:集群扩容、节点故障时,大量小分片需要在节点之间移动,总耗时比少分片更长。
问题排查更难:分片越多,出现未分配分片、分片损坏的概率越高,定位问题更麻烦。
快照备份/恢复速度变慢:快照需要遍历所有分片的元数据,分片越多备份耗时越长。

然后ES的读写并行能力是由主分片数量来进行控制的,当主分片数量越多,那么读写并发支持的越多,但是读写不一定更快。

这里简单说一下,当一台32C/64G 节点,NVMe SSD,单分片大小 20~30GB支持500-25000左右的QPS,这里根据写入的场景来划分、例如写入一个极为复杂的文档,且写入的数据量超过10KB,那么QPS可能只有500 ~ 3000,当写入一个简单的文档,例如日志等,那么QPS可能为8000-25000左右的QPS

主分片存储数据量大小控制

上文有说过,主分片存储数据量大小推荐为20-50GB,那么怎么控制主分片存储的数据量大小呢?

  • 方式一:预估主分片数量
    在创建索引之前,需要预估一下该索引可能会存储多少数据?例如该索引会存储100GB数据,那么有3个集群节点,则这个索引给3个主分片即可

  • 方式二:使用Rollover + ILM自动滚动(推荐)
    最适合日志/指标/订单等按时间生成的时序数据,ES自动监控分片大小/时间,超过阈值自动生成新索引,永远不会出现超大分片:

示例:

PUT _ilm/policy/log_ilm_policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "30GB",  // 单分片到30G自动滚动
            "max_age": "7d",     // 或者满7天自动滚动,哪个条件先到就触发
            "max_docs": 10000000 // 或者满1000万条自动滚动
          }
        }
      },
      "delete": {
        "min_age": "30d", // 超过30天自动删除
        "actions": { "delete": {} }
      }
    }
  }
}

当索引的分片存储数据量太大怎么解决呢?

方案A:Split拆分(推荐,速度快,不需要全量reindex)

适用条件:分片数只能拆成原分片数的整数倍,比如原4分片可以拆成8/12/16个,不能拆成6个

# 1. 先把索引设为只读
PUT /超大索引名/_settings
{ "index.blocks.write": true }

# 2. 拆分,原4分片拆成8分片(必须是整数倍)
POST /超大索引名/_split/拆分后的新索引名
{
  "settings": {
    "index.number_of_shards": 8
  }
}

方案B:Reindex重建(通用,任何情况都适用)

适用条件:任意调整分片数,没有倍数限制,缺点是需要全量同步数据,数据量大的话耗时久

# 1. 创建新索引,设置合适的分片数
PUT /新索引名
{
  "settings": {
    "number_of_shards": 10, // 改成你需要的分片数
    "number_of_replicas": 1
  }
}
# 2. 全量同步数据
POST _reindex?wait_for_completion=true
{
  "source": { "index": "超大索引名" },
  "dest": { "index": "新索引名" }
}

当索引分片太小(碎片太多)时怎么解决呢?

方案A:Shrink合并(推荐,速度快,不需要全量reindex)

适用条件:分片数只能合并成原分片数的约数,比如原8分片可以合并成4/2/1个,不能合并成3个

限制:Shrink 时不能指定 mapping,只能沿用源索引 mapping
源索引必须所有分片 结构一致、正常 green、只读

# 1. 先把索引设为只读,并且所有分片移到同一个节点
PUT /小分片索引名/_settings
{
  "index.blocks.write": true,
  "index.routing.allocation.require._name": "你的节点名称"
}
# 2. 合并,原8分片合并成2分片(必须是约数)
POST /小分片索引名/_shrink/合并后的新索引名
{
  "settings": {
    "index.number_of_shards": 2
  }
}

方案B:多个小索引合并成一个大索引

适用条件:比如把7天的日索引(log-2026.04.10 ~ log-2026.04.16)合并成一个周索引,减少总分片数

_reindex 合并多个 mapping 不同的索引时,需要下面几点注意:
1、新索引必须提前创建好统一的 mapping
2、所有旧索引的数据都会往这个新 mapping 上靠
3、类型冲突时:旧数据类型 vs 新 mapping 类型不兼容 → 报错,兼容 → 自动转换
当旧索引有、新索引没有的字段 → 被丢弃(不写入)
旧索引没有、新索引有的字段 → 写入 null / 默认值

// 1. 创建新的周索引,设置合适的分片数
PUT /log-2026.04.week2
{ "settings": { "number_of_shards": 6 } }
// 2. 批量同步所有日索引数据
POST _reindex?wait_for_completion=true
{
  "source": { "index": ["log-2026.04.10","log-2026.04.11","log-2026.04.12","log-2026.04.13","log-2026.04.14","log-2026.04.15","log-2026.04.16"] },
  "dest": { "index": "log-2026.04.week2" }
}

文档的写入流程

ES中文档的写入和主分片有关系,我们可以来看下整个文档的写入流程

ES文档写入涉及到下面几个组件:

  • Client:客户端(REST/Java API)
  • Coordinating Node:协调节点(接收请求、路由、返回结果)
  • Primary Shard:主分片(写入核心,数据强一致)
  • Replica Shard:副本分片(异步 / 同步复制,高可用)
  • Memory Buffer(Index Buffer):内存缓冲(高速写入,不可搜索)
  • Translog:事务日志(断电恢复,数据不丢)
  • Segment(Lucene):段文件(倒排索引,可搜索)
  • Disk:持久化磁盘

其写入流程主要分为7个步骤:

  • 1、客户端发起请求->ES协调节点写入
    • 客户端发送:PUT /my_index/_doc/1
    • 请求到达任意节点,该节点成为 Coordinating Node(协调节点)
  • 2、路由计算 → 确定主分片
    • 默认按照_id哈希,当用户指定id时,会按照id字段哈希
    • 然后协调节点查询集群元数据,找到该主分片所在节点
      _id哈希公式:
shard_num = hash(_id) % number_of_primary_shards
  • 3、转发请求 → 主分片节点

    • 协调节点把请求转发给 主分片所在的数据节点
  • 4、主分片写入(核心!内存 + Translog)
    主分片所在节点执行:

    • 写入 Memory Buffer(Index Buffer)
      • 数据进内存,此时不可搜索
      • 写入速度极快(内存级)
    • 同步写入 Translog(事务日志)
      • 每条写入都追加到 Translog,并刷到磁盘(默认 request 模式,强一致)
      • 作用:宕机后从 Translog 恢复内存数据,防止丢失
  • 5、主分片 → 副本分片 同步

    • 主分片并行把请求发给其对应的所有副本分片
    • 副本分片执行和主分片一样的逻辑:写内存 + 写 Translog
    • 副本全部成功后,主分片才标记写入成功
  • 6、返回结果 → 客户端

    • 主分片向协调节点返回成功
    • 协调节点向客户端返回 201 Created
  • 7、后台异步(Refresh + Flush)
    7.1 Refresh(默认 1 秒,近实时搜索)

    • 触发:refresh_interval: 1s 或内存满
    • 动作:
      • Memory Buffer → 生成 新 Lucene Segment(倒排索引)
      • Segment 写入 OS Cache(文件系统缓存),不直接落盘
      • 此时文档可被搜索(NRT 近实时)
      • 清空 Memory Buffer
        7.2 Flush(默认 30 分钟,持久化)
    • 触发:30 分钟、Translog 达 512MB、手动 flush
    • 动作:
      • 所有内存 Segment → 强制刷到磁盘
      • 执行 Lucene Commit,生成 commit 点
      • 清空 Translog,释放空间

副本分片概述

ES中的副本分片是主分片的完全拷贝,数据上和主分片完全一致。

可以看上面文档的写入流程,当文档完全写入主分片和副本分片时才会标记文档写入成功

副本分片是实现ES高可用的基础,当主分片挂了之后,副本分片会自动升级为主分片。

此时有一个问题,当主分片挂了之后,副本分片升级为主分片之后,那么还会有主分片吗?
主分片挂了之后,可以理解成当前主分片的节点挂了,副本分片升级为主分片之后,此时是集群为yellow,是没有副本分片的。
主要当原主分片所在的节点恢复之后,此时整个集群变为green,副本分片才会恢复。

并且副本分片可以承载ES中读的请求,提高ES集群查询的吞吐量。

ES中同一份数据的主分片和副本分片永远不会分配在同一个节点上(ES内置反亲和规则,避免单节点故障同时丢主副)

例如当前集群有三个节点,主分片在A节点上,那么副本分片永远不会在A节点上,只会在B或者C节点中。

副本数可以动态调整,随时修改,不需要重建索引
示例:

# 创建一个索引,指定副本分片为1
PUT /test01
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}
# 查看一下
GET /test01/_settings
# 返回结果
{
  "test01" : {
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "3",
        "provided_name" : "test01",
        "creation_date" : "1776394680601",
        "number_of_replicas" : "1", # 副本分片数量
        "uuid" : "FSLjaib0Rv2JXvd_wlg2MQ",
        "version" : {
          "created" : "7172699"
        }
      }
    }
  }
}


# 修改副本分片数量
PUT /test01/_settings
{
  "settings": {
    "number_of_replicas": 2 #修改为2
  }
}

# 查看一下
GET /test01/_settings
{
  "test01" : {
    "settings" : {
      "index" : {
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "3",
        "provided_name" : "test01",
        "creation_date" : "1776394680601",
        "number_of_replicas" : "2", # 主分片为2
        "uuid" : "FSLjaib0Rv2JXvd_wlg2MQ",
        "version" : {
          "created" : "7172699"
        }
      }
    }
  }
}

副本分片设置注意事项

  • 不要把副本数设为0(临时导入数据除外):设0主分片挂了直接丢数据,导入完成后立刻改回1
  • 不要设超过2副本:3副本及以上性价比极低,写性能下降明显(写请求要同步到所有副本),存储成本过高,除非是金融级核心业务

可以给一个设置的参考

集群规模 业务等级 推荐副本数 说明
1节点测试集群 测试 0 单节点设副本也分配不了,浪费空间
2节点生产集群 普通业务 1 必须设1副本,任意节点挂了数据不丢
3节点及以上生产集群 普通业务 1 性价比最高,兼顾高可用和存储成本
3节点及以上生产集群 核心业务(不能停机) 2 允许同时挂2个节点还能正常提供服务,存储成本是1副本的1.5倍

文档的读取流程

文档的读取流程分为两种,一种是GET流程,一种是SEARCH流程。

文档的GET读取流程较于写入流程比较简单,其流程如下:

  • 客户端发起GET请求
GET /my_index/_doc/100
  • 请求到达协调节点
    任何一个节点都可以当协调节点,只负责路由 + 转发 + 聚合。

  • 协调节点 计算文档所在分片
    根据公式计算完成之后协调节点立刻知道:文档 100 只在 主分片 P2 及其副本 R2 上
    计算公式:

shard = hash(_id) % 主分片数
  • 协调节点 选择分片(负载均衡)
    它会在 主分片 + 所有副本分片中进行轮询(round-robin) 选一个:

    • 可能选 P2
    • 可能选 R2
  • 协调节点转发请求到选中的分片节点上
    该分片本地读取文档,直接返回。

  • 分片返回文档 → 协调节点 → 客户端

文档的SEARCH读取流程较于写入流程比较复杂

search分为 2 个阶段:

  • Query 阶段(分片本地查,只返回 id + 排序值)
  • Fetch 阶段(协调节点拉取真实文档)
第 1 阶段:Query 阶段(分散收集)
  • 客户端发起请求到协调节点
  • 协调节点把查询广播到 所有主分片 + 副本分片
  • 每个分片开始本地执行搜索
    • 匹配文档
    • 按相关性分数排序
  • 每个分片只返回:doc id + 分数 + 排序值
    • 注意:不返回完整文档! 轻量级
  • 查询结果全部返回给协调节点
Fetch 阶段(聚合拉取)
  • 协调节点把所有分片返回的 id进行:
    • 全局排序
    • 合并
  • 协调节点取最终 top N(如前 10 条)
  • 协调节点根据这 最终 topN 的 id,向对应分片发起获取真实文档请求
  • 对应分片返回完整文档内容
  • 协调节点封装结果 → 返回给客户端

search读取文档的关键点:

  • 搜索 = 查所有分片
    • 不管数据在哪,必须广播所有分片
  • 分片越多,搜索压力越大
  • 副本可以分担搜索压力
    • 协调节点会轮询主 / 副,实现搜索负载均衡
  • Query 只传 id,Fetch 才取文档
    • 减少网络传输,这是 ES 搜索快的原因之一
  • from + size 越深,性能越差,因为要合并的结果越多

超级汇总:主分片和副本分片的区别

对比项 主分片(Primary Shard) 副本分片(Replica Shard)
数据角色 原始数据、权威数据源 主分片的完整备份拷贝
是否接受写入 接受写入(唯一入口) 不接受写入,仅同步数据
数量是否可修改 创建后不可修改 可动态修改(随时增删)
故障影响 挂掉会导致分片不可用,需副本切换 挂掉不影响写入,仅降低可用性
高可用作用 提供完整数据,被副本依赖 主分片挂掉可升级为主分片
查询作用 提供查询服务 分担查询,提升并发能力
同节点分配 与自己的副本不能在同一节点 与对应主分片不能在同一节点