ES101系列07 | 分布式系统和分页

article/2025/6/7 13:59:55

本篇文章主要讲解 ElasticSearch 中分布式系统的概念,包括节点、分片和并发控制等,同时还会提到分页遍历和深度遍历问题的解决方案。

节点

  • 节点是一个 ElasticSearch 示例
    • 其本质就是一个 Java 进程
    • 一个机器上可以运行多个示例但生产环境推荐只运行一个
  • 每一个节点都有名字,通过配置文件配置
  • 每一个节点启动后都会分配一个 UID,保存在 data 目录下
Coordinating Node
  • 处理请求的节点,叫 Coordinating Node
    • 路由请求到正确的节点,例如创建索引的请求,需要路由到 Master
  • 所有节点默认都是 Coordinating Node
  • 通过将其他类型设置成 False,使其成为 Dedicated Coordinating Node
Data Node
  • 可以保存数据的节点,叫做 Data Node
    • 节点启动后,默认就是数据节点。可以设置 node.data:false 禁止
  • Data Node 的职责
    • 保存分片数据。在数据扩展上起到了至关重要的作用(由 Master Node 决定如何把分片分发到数据节点上)
  • 通过增加数据节点
    • 可以解决数据水平扩展和解决数据单点问题
Master Node
  • Master Node 的职责
    • 处理创建,删除索引等请求/决定分片被分配到哪个节点 /负责索引的创建与删除
    • 维护并且更新 Cluster State
  • Master Node 的最佳实践
    • Master 节点非常重要,在部署上需要考虑解决单点的问题
    • 为一个集群设置多个 Master 节点/每个节点只承担 Master 的单一角色
Master Eligible Nodes
  • 一个集群,支持配置多个 Master Eligible 节点。这些节点可以在必要时(如 Master 节点出现故障,网络故障时)参与选主流程,成为 Master 节点
  • 每个节点启动后,默认就是一个 Master Eligible 节点
    • 可以设置 node.master: false 禁止
  • 当集群内第一个 Master Eligible 节点启动时候,它会将自己选举成 Master 节点
选主过程
  • 互相 Ping 对方,Node ld 低的会成为被选举的节点
  • 其他节点会加入集群,但是不承担 Master 节点的角色。一旦发现被选中的主节点丢失,就会选举出新的 Master 节点
脑裂问题
  • Split-Brain,分布式系统的经典网络问题,当出现网络问题,一个节点和其他节点无法连接
    • Node 2 和 Node 3 会重新选举 Master
    • Node 1 自己还是作为 Master 组成一个集群,同时更新 Cluster State
    • 导致 2 个 Master 维护不同的 Cluster State,当网络恢复时,无法选择正确恢复

解决方法

  • 限定选举条件,设置 quorum(仲裁),只有当 Master Eligible 节点数大于 quorum 时才能进行选举
  • 7.0 后无需配置

分片

Primary Shard
  • 分片是 ElasticSearch 分布式存储的基石(主分片 / 副本分片)
  • 通过主分片,将数据分布在所有节点上
    • Primary Shard 可以将一份索引的数据分散在多个 Data Node 上,实现存储的水平扩展
    • 主分片数在索引创建时候指定,后续默认不能修改,如要修改需重建索引
Replica Shard
  • 数据可用性
    • 通过引入副本分片(Replica Shard)提高数据的可用性。一旦主分片丢失,副本分片可以 Promote 成主分片。副本分片数可以动态调整。每个节点上都有完备的数据。如果不设置副本分片,一旦出现节点硬件故障,就有可能造成数据丢失
  • 提升系统的读取性能
    • 副本分片由主分片(Primary Shard)同步。通过支持增加 Replica 个数,一定程度可以提高读取的吞吐量
分片数的设定
  • 如何规划一个索引的主分片数和副本分片数
    • 主分片数过小:例如创建了 1 个 Primary Shard 的 Index。如果该索引增长很快,集群无法通过增加节点实现对这个索引的数据扩展
    • 主分片数设置过大:导致单个 Shard 容量很小,引发一个节点上有过多分片,影响性能
    • 副本分片数设置过多,会降低集群整体的写入性能
集群健康状态
GET /_cluster/health{"cluster_name" : "lanlance","status" : "green","timed_out" : false,"number_of_nodes" : 2,"number_of_data_nodes" : 2,"active_primary_shards" : 21,"active_shards" : 42,"relocating_shards" : 0,"initializing_shards" : 0,"unassigned_shards" : 0,"delayed_unassigned_shards" : 0,"number_of_pending_tasks" : 0,"number_of_in_flight_fetch" : 0,"task_max_waiting_in_queue_millis" : 0,"active_shards_percent_as_number" : 100.0
}
  • Green:健康状态,所有的主分片和副本分片都可用
  • Yellow:亚健康,所有的主分片可用,部分副本分片不可用
  • Red:不健康状态,部分主分片不可用
文档到分片的路由算法
  • s h a r d = h a s h ( r o u t i n g ) / 主分片数 shard = hash(routing) / 主分片数 shard=hash(routing)/主分片数
    • Hash 算法确保文档均匀分散到分片中
    • 默认 routing 值是文档 id
    • 可以自行制定 routing 值,与业务逻辑绑定也可以
    • 是 Primary Shard 数不能修改的根本原因

删除一个文档的流程

分片的内部原理
倒排索引的不可变性

倒排索引采用 Immutable Design,一旦生成,不可更改

不可变性,带来了的好处如下:

  • 无需考虑并发写文件的问题,避免了锁机制带来的性能问题
  • 一旦读入内核的文件系统缓存,便留在哪里。只要文件系统存有足够的空间,大部分请求就会直接请求内存,不会命中磁盘,提升了很大的性能
  • 缓存容易生成和维护/数据可以被压缩

但坏处是如果需要让一个新的文档可以被搜索,需要重建整个索引。

Lucene Index
  • 在 Lucene 中,单个倒排索引文件被称为 Segment。Segment 是自包含的,不可变更的。多个 Segments 汇总在一起称为 Lucene 的 Index,其对应的就是 ES 中的 Shard
  • 当有新文档写入时,会生成新 Segment,查询时会同时查询所有 Segments,并且对结果汇总。Lucene 中有一个文件用来记录所有 Segments 信息,叫做 Commit Point

Refresh
  • 将 Index buffer 写入 Segment 的过程叫 Refresh。Refresh 不执行 fsync 操作
  • Refresh 默认 1 秒发生一次,可通过 index.refresh_interval 配置。Refresh 后数据就可以被搜索到了。这也是为什么 ElasticSearch 被称为近实时搜索
  • 如果系统有大量的数据写入,那就会产生很多的 Segment
  • Index Buffer 被占满时会触发 Refresh,默认值是 JVM 的 10%
Transaction Log
  • Segment 写入磁盘的过程相对耗时,借助文件系统缓存,Refresh 时先将 Segment 写入缓存以开放查询
  • 为了保证数据不会丢失,所以在 Index 文档时同时写 Transaction Log,高版本开始 Transaction Log 默认落盘。每个分片有一个 Transaction Log
  • 在 ES Refresh 时 Index Buffer 被清空,Transaction log 不会清空
Flush
  • 调用 Refresh,清空 Index Buffer
  • 调用 fsync,将缓存中的 Segments 写入磁盘
  • 清空 Transaction Log

默认 30 分钟调用一次,当 Transaction Log 满时(默认 512 MB)也会调用

Merge
  • Segment 很多,需要被定期合并
    • 减少 Segments / 真正删除已经删除的文档
  • ES 和 Lucene 会自动进行 Merge 操作
    • POST my_index / _forcemerge
分布式搜索的运行机制

ElasticSearch 的搜索会分为 Query 和 Fetch 两阶段进行。

Query
  • 用户发出搜索请求到 ES 节点。节点收到请求后,会以 Coordinating 节点的身份,在 6 个主副分片中随机选择 3 个分片,发送查询请求。
  • 被选中的分片执行查询,进行排序。每个分片都会返回 From+Size 个排序后的文档 Id 和排序值给 Coordinating 节点。
Fetch
  • Coordinating Node 会将 Query 阶段从每个分片获取的排序后的文档 Id 列表重新进行排序。选取 From 到 From+Size 个文档的 Id。
  • 以 multiget 请求的方式到相应的分片获取详细的文档数据。

潜在有性能不好和相关性算分不准的问题。

解决算分不准的问题
  • 数据量不大的时候主分片数设置为 1,数据量大的时候保证文档均匀分散在各个分片上。
  • 使用 DFS Query Then Fetch。会进行一次完整的相关性算法,耗费更多资源,性能不好。
排序
  • 排序是针对字段原始内容进行的,倒排索引无法发挥作用,需要正排索引。
  • ElasticSearch 中有两种实现方法。
    • FieldData
    • Doc Values(列式存储,对 Text 类型无效)

Doc Values 和 Field Data 比较:

特性Doc ValuesField Data
存储位置磁盘 (内存映射访问)堆内存 (JVM Heap)
加载时机按需加载 (惰性加载到 OS 缓存)按需构建 (首次用于聚合/排序时构建在内存中)
数据结构列式存储 (按文档 ID 组织值)列式存储 (按段构建)
适用字段keyword, numeric, date, ip, booleantext (默认关闭),其他字段类型 (已废弃)
默认启用 (对于支持它的字段类型) (尤其对于 text 字段,7.0+ 默认关闭)
内存占用 (利用 OS 文件缓存,不直接占用 JVM 堆) (直接占用 JVM 堆内存)
垃圾回收无影响 (由 OS 管理缓存)显著影响 (对象在堆上,易引发 GC 压力)
适用操作聚合、排序、脚本 (高效)text 字段聚合 (分词后的词条)
安全性 (不易引发 OOM) (不当配置易导致节点 OOM)
版本趋势推荐并默认仅限 text 字段聚合需求 (其他字段已弃用)

分页和遍历

分布式系统中深度分页的问题
  • ES 天生就是分布式的。查询信息同时数据保存在多个分片、多台机器上,ES 天生就需要满足排序的需要(按照相关性算分)。
  • 当一个查询:From=990,Size =10。会在每个分片上先都获取 1000 个文档。通过 Coordinating Node 聚合所有结果。最后再通过排序选取前 1000 个文档。
  • 页数越深,占用内存越多。为了避免深度分页带来的内存开销。ES 有一个设定,默认限定到 10000 个文档。
使用 Search After 避免深度分页问题
  • 避免深度分页的性能问题,可以实时获取下一页文档信息
    • 不支持指定页数 (From)
    • 只能往下翻
  • 第一步搜索需要指定 sort,并且保证值是唯一的 (可以通过加入 id 保证唯一性)
  • 然后使用上一次最后一个文档的 sort 值进行查询。

示例

1、插入数据

POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":11}
POST users/_doc
{"name":"user2","age":12}
POST users/_doc
{"name":"user2","age":13}

2、执行查询

POST users/_search
{"size": 1,"query": {"match_all": {}},"sort": [{"age": "desc"} ,{"_id": "asc"}    ]
}POST users/_search
{"size": 1,"query": {"match_all": {}},"search_after":[10,"ZQ0vYGsBrR8X3IP75QqX"],"sort": [{"age": "desc"} ,{"_id": "asc"}    ]
}
Scroll API

Scroll API 是 Elasticsearch 为大数据集深度遍历设计的查询机制,通过创建快照式上下文(Snapshot Context)保证分页一致性,适用于离线导出、全量迁移等场景。

示例

DELETE users
POST users/_doc
{"name":"user1","age":10}
POST users/_doc
{"name":"user2","age":20}
POST users/_doc
{"name":"user3","age":30}
POST users/_doc
{"name":"user4","age":40}POST /users/_search?scroll=5m
{"size": 1,"query": {"match_all" : {}}
}// 这条数据无法查到
POST users/_doc
{"name":"user5","age":50}POST /_search/scroll
{"scroll" : "1m","scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAWAWbWdoQXR2d3ZUd2kzSThwVTh4bVE0QQ=="
}

Scroll API 与 Search After 的对比

特性Search AfterScroll API
设计目标实时深度分页(用户交互场景)大数据集离线遍历(导出/迁移)
实时性基于当前索引状态(实时可见变更)快照冻结(创建后索引变更不可见)
内存消耗低(无服务端状态)高(服务端维护上下文,占用堆内存)
分页一致性依赖 PIT 保障一致性天然一致性(快照隔离)
适用场景用户界面逐页浏览(如订单列表翻页)全量数据导出、ETL 迁移、离线分析
是否支持跳页❌ 仅顺序连续分页❌ 仅顺序连续遍历
资源释放无状态(客户端自主管理游标)需显式删除 Scroll ID(否则超时释放)
性能开销低(分片级游标定位)中(维护上下文,但比 from/size 高效)
最大深度仅受文档总数限制同左
推荐排序方式业务字段 + _id(确保唯一性)["_doc"](最高效,避免排序计算)
版本演进主流实时分页方案(结合 PIT 使用)逐渐被 Async Search 替代(大数据异步查询)

并发控制

ES 使用乐观锁进行并发控制。

ES 的乐观并发控制

ES 中的文档是不可变更的。如果你更新一个文档,会将就文档标记为删除,同时增加一个全新的文档。同时文档的 version 字段加 1。

示例

DELETE products
PUT products
PUT products/_doc/1
{"title":"iphone","count":100
}// success
PUT products/_doc/1?if_seq_no=1&if_primary_term=1
{"title":"iphone","count":100
}// fail
PUT products/_doc/1?if_seq_no=1&if_primary_term=1
{"title":"iphone","count":102
}// success
PUT products/_doc/1?version=30000&version_type=external
{"title":"iphone","count":100
}

写在最后

这是该系列的第七篇,主要讲解 ElasticSearch 中分布式系统的概念,包括节点、分片和并发控制等,同时提到了分页遍历和深度遍历问题的解决方案。可以自己去到 Kibana 的 Dev Tool 实战操作,未来会持续更新该系列,欢迎关注👏🏻。

同时欢迎关注小红书:LanLance。不定时分享职场思考、大厂方法论和后端经验❤️

参考

  1. https://github.com/onebirdrocks/geektime-ELK/
  2. https://www.elastic.co/elasticsearch/

http://www.hkcw.cn/article/ujWFFPqBui.shtml

相关文章

RabbitMQ在SpringBoot中的应用

1.简单模式 P:生产者,要发送消息的程序. C:消费者,消息的接收者. Queue:消息队列,类似于一个邮箱,生产者向其中投递消息,消费者从其中取出消息. 特点:一个生产者P,一个消费者C,消息只能被消费一次,也成为点对点模式. 适用场景:消息只能被单个消费者处理. 设置队列的名称为…

基于python写的目录/文件递归检索工具

核心功能 1. 目录结构检索 递归扫描 :深度遍历指定目录及其所有子目录 多种检索模式 : 仅文件夹模式:只显示目录结构仅文件模式:只显示文件列表文件文件夹模式:完整显示目录树结构(默认模式) …

Qwen3高效微调

高效微调 场景、模型、数据、算力 高效微调的应用场景 对话风格微调:高效微调可以用于根据特定需求调整模型的对话风格。例如,针对客服系统、虚拟助理等场景,模型可以通过微调来适应不同的 语气、礼貌程度 或 回答方式,从而在与…

不动产登记区块链系统(Vue3 + Go + Gin + Hyperledger Fabric)

好久没有介绍过新项目的制作了,之前做的一直都是Fisco Bcos的项目,没有介绍过Hyperledger Fabric的项目,这次来给大家分享下。 系统概述 不动产登记与交易平台是一个基于Hyperledger Fabric的综合性管理系统,旨在实现不动产登记…

深度学习学习率调度器指南:PyTorch 四大 scheduler 对决

在深度学习模型训练中,学习率调度器(Learning Rate Scheduler)是影响模型收敛效果和训练稳定性的关键因素。选择合适的学习率调度策略,往往能让模型性能产生质的飞跃。本文将深入对比PyTorch中最常用的四种学习率调度器&#xff0…

ERP学习-AP

业务需要。持续更新学习进度 借助网上零搭建平台上手实操 这个是简道云平台页面链接,登录的化去手机号登录 目前开始对应付模块进行学习

基于 ZYNQ UltraScale+ OV5640的高速图像传输系统设计,支持国产替代

引 言 随着电子信息技术的不断进步,人工智能、医 疗器械、机器视觉等领域都在高速发展 [1] ,工业相机 是机器视觉系统中的一部分 [2] ,对工业相机而言,传 输图像的速率、传输过程的抗干扰能力是其关键, 工业相…

smartGit 试用突破30天

1 下载 选择19.1版本 2 运行 我是linux环境 解压后, cd bin ./smartgit.sh 选择使用30天. 然后退出 3 绿色软件 破解步骤 下载破解文件:访问 Gitee 链接 goto下载下载破解文件 解压文件:下载后解压得到 crackSmartGit.jar 和 license.zip 用编辑…

一、基础环境配置

一、虚拟机 主:192.168.200.200 从:192.168.200.201 从:192.168.200.202 二、docker docker基础搭建,有不会的自行百度。 1.目录结构 /opt/software:软件包/opt/module:解压包,自定义脚本…

Java面试八股--08-数据结构和算法篇

1、怎么理解时间复杂度和空间复杂度 时间复杂度和空间复杂度一般是针对算法而言,是衡量一个算法是否高效的重要标准。先纠正一个误区,时间复杂度并不是算法执行的时间,在纠正一个误区,算法不单单指冒泡排序之类的,一个…

Oracle中的循环——FOR循环、WHILE循环和LOOP循环

目录 一、FOR循环 1.FOR循环语法结构 二、WHILE循环 1.WHILE循环语法结构 三、LOOP循环 1.LOOP循环语法结构 四、三个循环的区别(重要) Oracle中的循环常用的有:FOR循环、WHILE循环和LOOP循环 一、FOR循环 1.FOR循环语法结构 DECLARE --不声明变量&…

ubuntu 20.04挂载固态硬盘

我们有个工控机,可以接入一个固态硬盘。将固态硬盘插好后,就要进行挂载。在AI的指导下,过程并不顺利。记录如下: 1、检查硬盘是否被识别 安装好硬盘后,运行以下命令来检查Linux系统是否已识别新硬盘: …

SAP 自动编号的使用

1、NUMBER_RANGE_ENQUEUE用于锁定编号范围对象,防止多用户并发访问冲突 2、NUMBER_RANGE_DEQUEUE用于解锁已维护的编号范围对象。 3、此外,还提到了NUMBER_GET_NEXT函数模块,用于获取编号范围内的下一个号码。 文章目录 创建编号范围程序实现…

Python趣学篇:从零打造智能AI井字棋游戏(Python + Tkinter + Minimax算法)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《Python星球日记》 目录 🎮 前言一、项目概述与技术…

[定昌linux开发板]设置密码策略

找到etc/pam.d/目录下的common-password 2.先把common-password给复制一份,避免改错了 可以看到新增了一个common-password.bak文件 3.打开common-password,增加密码策略 输入: sudo vi /etc/pam.d/common-passwod 打开common-password文件 点击&…

Godot 敌人生成半径和围墙不匹配,导致敌人错误的生成在围墙外的解决代码

一、原视频 3. Preventing Invalid Spawning 二、原代码 func get_spawn_position():var player get_tree().get_first_node_in_group("player") as Node2Dif player null:return Vector2.ZEROvar spawn_position Vector2.ZEROvar random_direction Vector2.RIG…

LabVIEW磁悬浮轴承传感器故障识别

针对工业高端装备中主动磁悬浮轴承(AMB)的位移传感器故障检测需求,基于 LabVIEW 平台构建了一套高精度故障识别系统。通过集成品牌硬件与 LabVIEW 的信号处理能力,实现了传感器探头故障的实时监测与精准定位,解决了传统…

Qt开发:QThreadPool的介绍和使用

文章目录 一、QThreadPool 简介二、常用函数简介三、完整示例 一、QThreadPool 简介 QThreadPool 是 Qt 提供的用于高效管理线程资源的类。它通过线程池的方式管理和复用线程,适合处理大量、短时间运行的任务,避免频繁创建和销毁线程带来的性能开销。 常…

蚂蚁感冒--思维

1.相遇后不用考虑转头&#xff0c;继续走就可以 2.思维&#xff0c;不只是傻傻的模拟&#xff0c;要总结出规律&#xff0c;什么情况一定可以感染&#xff0c;然后感染之后再怎么这么样 P8611 [蓝桥杯 2014 省 AB] 蚂蚁感冒 - 洛谷 #include<bits/stdc.h> using names…

non-autoregressive sequence generation

非自回归non-autoregressive 传统rnn是autoregressive,而且encode和decode都是根据上一个input/output,这样花费的时间就和句子长度成正比 transformer的输入是并行的,但是decode阶段还是autoregressive 单纯把影像当成 NM 个独立像素去拟合&#xff0c;会缺乏像素之间的依赖…