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

推荐订阅源

腾讯CDC
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tenable Blog
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
TaoSecurity Blog
TaoSecurity Blog
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
I
Intezer
Security Latest
Security Latest
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
O
OpenAI News
A
Arctic Wolf
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
M
MIT News - Artificial intelligence
F
Full Disclosure
P
Privacy International News Feed
The GitHub Blog
The GitHub Blog
T
Troy Hunt's Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
Cloudbric
Cloudbric
大猫的无限游戏
大猫的无限游戏
Google Online Security Blog
Google Online Security Blog
Recent Announcements
Recent Announcements
H
Help Net Security
量子位
V
V2EX
美团技术团队
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Schneier on Security
V2EX - 技术
V2EX - 技术
D
Docker
博客园 - 【当耐特】
Project Zero
Project Zero
博客园 - 司徒正美

博客园_首页

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