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

news2025/6/6 0:03:27

本篇文章主要讲解 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.coloradmin.cn/o/2398383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring AI Advisor机制

Spring AI Advisors 是 Spring AI 框架中用于拦截和增强 AI 交互的核心组件,其设计灵感类似于 WebFilter,通过链式调用实现对请求和响应的处理5。以下是关键特性与实现细节: 核心功能 ‌1. 请求/响应拦截‌ 通过 AroundAdvisor 接口动态修…

Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南

前言 实践文章指南 vue微前端qiankun框架学习到项目实战,基座登录动态菜单及权限控制>>>>实战指南:Vue 2基座 Vue 3 Vite TypeScript微前端架构实现动态菜单与登录共享>>>>构建安全的Vue前后端分离架构:利用长Token与短Tok…

日语输入法怎么使用罗马字布局怎么安装日语输入法

今天帮客户安装日语输入法的时候遇到了一个纠结半天的问题,客户一直反馈说这个输入法不对,并不是他要的功能。他只需要罗马字的布局,而不是打出来字的假名。 片假名、平假名,就好像英文26个字母,用于组成日文单词。两…

数据结构:栈(Stack)和堆(Heap)

目录 内存(Memory)基础 程序是如何利用主存的? 🎯 静态内存分配 vs 动态内存分配 栈(stack) 程序执行过程与栈帧变化 堆(Heap) 程序运行时的主存布局 内存(Memo…

用 Vue 做一个轻量离线的“待办清单 + 情绪打卡”小工具

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…

PostgreSQL数据库备份

文章目录 pg_dump 和 pg_dumpall使用 pg_dump 备份单个数据库示例 使用 pg_dumpall 备份整个数据库集群基本用法 恢复备份恢复 pg_dump 备份恢复 pg_dumpall 备份 Tips pg_dump 和 pg_dumpall 在 PostgreSQL 中,pg_dump 和 pg_dumpall 是两个常用的备份工具&#x…

js-day7

JS学习之旅-day7 1.事件流1.1 事件流与两个阶段说明1.2 事件捕获1.3 事件冒泡1.4 阻止1.5 解绑事件 2. 事件委托3. 其他事件3.1 页面加载事件3.2 页面滚动事件3.3 页面尺寸事件 4. 元素尺寸与位置 1.事件流 1.1 事件流与两个阶段说明 事件流指的是事件完整执行过程中的流动路…

解决Vditor加载Markdown网页很慢的问题(Vite+JS+Vditor)

1. 引言 在上一篇文章《使用Vditor将Markdown文档渲染成网页(ViteJSVditor)》中,详细介绍了通过Vditor将Markdown格式文档渲染成Web网页的过程,并且实现了图片格式居中以及图片源更换的功能。不过,笔者发现在加载这个渲染Markdown网页的时候…

鸿蒙5.0项目开发——横竖屏切换开发

横竖屏切换开发 【高心星出品】 文章目录 横竖屏切换开发运行效果窗口旋转配置module.json5的orientation字段调用窗口的setPreferredOrientation方法案例代码解析Index1页面代码:EntryAbility在module.json5的配置信息:Index页面的代码信息&#xff1…

Triton推理服务器部署YOLOv8(onnxruntime后端和TensorRT后端)

文章目录 一、Trition推理服务器基础知识1)推理服务器设计概述2)Trition推理服务器quickstart(1)创建模型仓库(Create a model Repository)(2)启动Triton (launching triton)并验证是否正常运行(3)发送推理请求(send a inference request)3)Trition推理服务器架…

TDengine 的 AI 应用实战——电力需求预测

作者: derekchen Demo数据集准备 我们使用公开的UTSD数据集里面的电力需求数据,作为预测算法的数据来源,基于历史数据预测未来若干小时的电力需求。数据集的采集频次为30分钟,单位与时间戳未提供。为了方便演示,按…

NLP学习路线图(二十一): 词向量可视化与分析

在自然语言处理(NLP)的世界里,词向量(Word Embeddings)犹如一场静默的革命。它将原本离散、难以捉摸的词语,转化为稠密、富含语义的连续向量,为机器理解语言铺平了道路。然而,这些向…

如何配置mvn镜像源为华为云

如何配置mvn镜像源为华为云 # 查找mvn 配置文件 mvn -X help:effective-settings | grep settings.xml# 配置mvn镜像源为华为云,/home/apache-maven-3.9.5/conf/settings.xml文件路径需要根据上一步中查询结果调整 cat > /home/apache-maven-3.9.5/conf/setting…

多模态大语言模型arxiv论文略读(105)

UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ➡️ 论文标题:UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ➡️ 论文作者:Zhaowei…

Pyhton中的命名空间包(Namespace Package)您了解吗?

在 Python 中,命名空间包(Namespace Package) 是一种特殊的包结构,它允许将模块分散在多个独立的目录中,但这些目录在逻辑上属于同一个包命名空间。命名空间包的核心特点是:没有 __init__.py 文件&#xff…

Azure DevOps Server 2022.2 补丁(Patch 5)

微软Azure DevOps Server的产品组在4月8日发布了2022.2 的第5个补丁。下载路径为:https://aka.ms/devops2022.2patch5 这个补丁的主要功能是修改了代理(Agent)二进制安装文件的下载路径;之前,微软使用这个CND(域名为vstsagentpackage.azuree…

手摸手还原vue3中reactive的get陷阱以及receiver的作用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、实例是什么?二、new Prxoy三、实现代码1.引入代码2.读入数据 总结 前言 receiver不是为解决get陷阱而生,而是为解决Proxy中的this绑…

C++学习-入门到精通【13】标准库的容器和迭代器

C学习-入门到精通【13】标准库的容器和迭代器 目录 C学习-入门到精通【13】标准库的容器和迭代器一、标准模板库简介1.容器简介2.STL容器总览3.近容器4.STL容器的通用函数5.首类容器的通用typedef6.对容器元素的要求 二、迭代器简介1.使用istream_iterator输入,使用…

C# 面向对象特性

面向对象编程的三大基本特性是:封装、继承和多态。下面将详细介绍这三大特性在C#中的体现方式。 封装 定义:把对象的数据和操作代码组合在同一个结构中,这就是对象的封装性。 体现方式: 使用访问修饰符控制成员的可见性 通过属…

ElasticStack技术之logstash介绍

一、什么是Logstash Logstash 是 Elastic Stack(ELK Stack)中的一个开源数据处理管道工具,主要用于收集、解析、过滤和传输数据。它支持多种输入源,如文件、网络、数据库等,能够灵活地对数据进行处理,比如…