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

本篇文章主要讲解 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.pswp.cn/news/908203.shtml

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

相关文章

CppCon 2015 学习:3D Face Tracking and Reconstruction using Modern C++

1. 3D面部追踪和重建是什么? 3D面部追踪(3D Face Tracking): 实时检测并追踪人脸在三维空间中的位置和姿态(如转头、点头、表情变化等),通常基于摄像头捕获的视频帧。3D面部重建(3D…

代码中的问题及解决方法

目录 YOLOX1. AttributeError: VOCDetection object has no attribute cache2. ValueError: operands could not be broadcast together with shapes (8,5) (0,)3. windows远程查看服务器的tensorboard4. AttributeError: int object has no attribute numel YOLOX 1. Attribu…

【JVM】Java类加载机制

【JVM】Java类加载机制 什么是类加载? 在 Java 的世界里,每一个类或接口在经过编译后,都会生成对应的 .class 字节码文件。 所谓类加载机制,就是 JVM 将这些 .class 文件中的二进制数据加载到内存中,并对其进行校验…

vue的监听属性watch的详解

文章目录 1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景 1. 监听属性 watch watch 是一个对象,键是需要观察的表达式,用于观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数的参数是新值和旧值。值也可以是方法名&am…

如何在 Ubuntu 24.04 服务器上安装 Apache Solr

Apache Solr 是一个免费、开源的搜索平台,广泛应用于实时索引。其强大的可扩展性和容错能力使其在高流量互联网场景下表现优异。 Solr 基于 Java 开发,提供了分布式索引、复制、负载均衡及自动故障转移和恢复等功能。 本教程将指导您如何在 Ubuntu 24.…

Linux内核中TCP三次握手的实现机制详解

TCP三次握手是建立可靠网络连接的核心过程,其在内核中的实现涉及复杂的协议栈协作。本文将深入分析Linux内核中三次握手的实现机制,涵盖客户端与服务端的分工、关键函数调用、协议号验证及数据包处理流程。 一、三次握手的整体流程 三次握手分为三个阶段,客户端与服务端通过…

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…

优化 Spring Boot API 性能:利用 GZIP 压缩处理大型有效载荷

引言 在构建需要处理和传输大量数据的API服务时,响应时间是一个关键的性能指标。一个常见的场景是,即使后端逻辑和数据库查询已得到充分优化,当API端点返回大型数据集(例如,数千条记录的列表)时&#xff0…

【WPF】WPF 项目实战:构建一个可增删、排序的光源类型管理界面(含源码)

💡WPF 项目实战:构建一个可增删、排序的光源类型管理界面(含源码) 在实际的图像处理项目中,我们经常需要对“光源类型”进行筛选或管理。今天我们来一步步构建一个实用的 WPF 界面,实现以下功能&#xff1…

C++23 已弃用特性

文章目录 1. std::aligned_storage 与 std::aligned_union1.1 特性介绍1.2 被弃用的原因1.3 替代方案 2. std::numeric_limits::has_denorm2.1 特性介绍2.2 被弃用的原因 3. 总结 C23 已弃用特性包括:std::aligned_storage、std::aligned_union 与 std::numeric_lim…

十三、【核心功能篇】测试计划管理:组织和编排测试用例

【核心功能篇】测试计划管理:组织和编排测试用例 前言准备工作第一部分:后端实现 (Django)1. 定义 `TestPlan` 模型2. 生成并应用数据库迁移3. 创建 `TestPlanSerializer`4. 创建 `TestPlanViewSet`5. 注册路由6. 注册到 Django Admin第二部分:前端实现 (Vue3)1. 创建 `Test…

STM32最小CLion开发环境

文章目录 1 必须文件2 工具链3 CLion 全局配置4 CLion 新项目配置ST-Link 调试 5 点亮 LED6 分析 elf 文件7 项目模板 1 必须文件 ST 提供的头文件支持 MDK-ARM, GCC, IAR 3种编译器, 下面采用 GCC 编译器 Arm GNU Toolchain Downloads – Arm Developer 或 安装包版 调试器服…

核函数:解锁支持向量机的强大能力

在机器学习的世界中,支持向量机(SVM)是一种强大的分类算法,而核函数则是其背后的“魔法”,让 SVM 能够处理复杂的非线性问题。今天,我们就来深入探讨核函数的奥秘,看看它们是如何帮助 SVM 在高维…

【Go-6】数据结构与集合

6. 数据结构与集合 数据结构是编程中用于组织和存储数据的方式,直接影响程序的效率和性能。Go语言提供了多种内置的数据结构,如数组、切片、Map和结构体,支持不同类型的数据管理和操作。本章将详细介绍Go语言中的主要数据结构与集合&#xf…

3. 简述node.js特性与底层原理

😺😺😺 一、Node.js 底层原理(简化版) Node.js 是一个 基于 Chrome V8 引擎构建的 JavaScript 运行时,底层核心由几部分组成: 组成部分简要说明 1.V8 引擎 将 JS 编译成机器码执行&#xff0…

Web开发主流前后端框架总结

🖥 一、前端主流框架 前端框架的核心是提升用户界面开发效率,实现高交互性应用。当前三大主流框架各有侧重: React (Meta/Facebook) 核心特点:采用组件化架构与虚拟DOM技术(减少真实DOM操作,优化渲染性能&…

大语言模型备案与深度合成算法备案的区别与联系

“什么情况下做算法备案?” “什么情况下做大模型备案呢?” 进行大模型备案的企业必然要进行算法备案,而进行算法备案的企业则需根据其提供的服务性质判断是否需要进行大模型备案。 算法备案与大模型备案已经是个老生常谈的话题了&#xf…

微软PowerBI考试 PL300-Power BI 入门

Power BI 入门 上篇更新了微软PowerBI考试 PL-300学习指南,今天分享PowerBI入门学习内容。 简介 Microsoft Power BI 是一个完整的报表解决方案,通过开发工具和联机平台提供数据准备、数据可视化、分发和管理。 Power BI 可以从使用单个数据源的简单…

【Hive入门】

之前实习写的笔记,上传留个备份。 1. 使用docker-compose快速搭建Hive集群 使用docker快速配置Hive环境 拉取镜像 2. Hive数据类型 隐式转换:窄的可以向宽的转换显式转换:cast 3. Hive读写文件 SerDe:序列化(对象转为字节码…

设计模式——简单工厂模式(创建型)

摘要 本文主要介绍了简单工厂模式,包括其定义、结构、实现方式、适用场景、实战示例以及思考。简单工厂模式是一种创建型设计模式,通过工厂类根据参数决定创建哪一种产品类的实例,封装了对象创建的细节,使客户端无需关心具体类的…