当前位置: 首页 > news >正文

Elasticsearch 写入全链路:从单机到集群

0. 先把术语摆正

  • Index(索引):逻辑数据集合,≈ MySQL 的库。
  • Document(文档):一条 JSON 数据,≈ MySQL 的行。
  • Field(字段):文档里的键值,≈ MySQL 的列。
  • Shard(分片):一个索引被水平拆成 N 份(primary主 + replica副本)。
  • Segment:Lucene 最小存储单元(文件不可变);很多机制围着它转。
  • 倒排索引:term → posting list(文档 ID 列表)。
  • Doc Values:列式存储,用于排序/聚合(不是倒排索引)。
  • File System Cache:OS 级缓存,并非落盘。

ES 搜索是近实时(NRT),不是强实时。写入到可被搜索看到,中间隔着一个 refresh


1. 单机写入:一条写请求到底发生了什么?

1.1 写入过程(Index/Insert/Update)

  1. Analyzer 分析:对 text 字段分词(如 IK),keyword/数值/日期 不分词;可能生成多字段(text+keyword)。

  2. 写入内存 buffer:用于构建倒排索引/列存结构;此时搜索不可见

  3. 追加 translog:同时把这次操作追加写入 translog 文件(磁盘上的操作日志文件,但默认先进 OS page cache,有个参数控制什么时候真正落盘,1.3会提到,其实默认就是直接刷盘)。

  4. refresh(默认 1s):把内存 buffer 的内容刷新为新的 segment

    • 新 segment 进入 file system cache,从此搜索可见
    • 这一步不是持久化,只是“可被搜索到”。
  5. flush(定期或阈值触发):强制把缓存中的 segment 落到磁盘,清空 translog,形成安全的持久化点(commit point)。

  6. merge(后台线程):自动把多个小 segment 合并成更大的 segment,清理删除标记,降低查询开销。

1.2 GET-by-id 与搜索的可见性不同

  • GET index/_doc/id:默认实时realtime=true),即使没 refresh,也能从 translog 读取最新版本。
  • _search 查询:需要 refresh 后的新 segment 才能看到(NRT)。

1.3 translog 的持久化策略

  • index.translog.durability=request(默认):每次写请求结束前执行 fsync,确保 translog 已真正落到物理盘

    • 安全但慢。
  • index.translog.durability=async:写入 translog 后不立刻 fsync,按 index.translog.sync_interval(默认 5s)定期批量 fsync。

    • 快,但节点崩溃时可能丢失最近一个 sync_interval 窗口的数据。

重点区分:“写入 translog 文件” ≠ “fsync 到磁盘盘片”。前者可能仅到 OS page cache;后者才是不可丢的落盘。

1.4 refresh / flush / merge 的触发与作用

  • refresh

    • 触发:周期(index.refresh_interval,默认 1s)/ 手动 / 写多时可临时关闭(设为 -1)再打开批量refresh以提速。
    • 作用:生成新 segment,使数据可被搜索
  • flush

    • 触发:定时(常见 30min 级别)、translog 过大、手动。
    • 作用:把 segment 真正落盘清空 translog,形成安全恢复点。注意,translog只是操作日志而不是实际数据segment的落盘。
  • merge

    • 触发:Lucene 的合并策略自动决定(如分层合并,逐级把小段并大)。
    • 作用:减少 segment 数/删除开销,提升搜索性能,但会吃 IO/CPU。

简要心智图:
写 → buffer + translog →(1s)refresh(可查但未持久化)→(周期/阈值)flush(持久化并清空 translog)→(后台)merge(优化查询)。


2. 从单机到集群:协调、路由与复制

2.1 写入复制流程(Primary-Replica)

  1. 客户端 → 任意节点(作为协调节点)。
  2. 协调节点计算目标 主分片,把请求转发过去。
  3. 主分片执行写入(buffer + translog),并把操作并行转发给所有副本分片。
  4. 副本分片执行同样的写入(buffer + translog)。
  5. 所有副本 ACK 后,主分片返回给协调节点 → 客户端。

注意:一条文档只会写入它所属的 一个主分片 + 其副本,不是所有分片。

2.2 协调节点(Coordinating Node)是怎么来的?

  • 不是选举出来的固定角色:谁接到客户端请求,谁临时扮演协调节点。

  • 为什么需要它

    1. 屏蔽路由细节(客户端不必知道分片在哪个节点),如果没有他,客户端就必须保存每个内容的主分片在哪个节点,这肯定不合理。。
    2. 适应集群动态(分片会迁移/升副为主)。
    3. 压力均衡(请求可打到任意节点,由它路由)。

2.3 路由规则与分片定位

  • 所有节点都知道这两个内容:

    • 路由算法shard = hash(_id) % number_of_primary_shards
    • 分片位置:通过 Cluster State(由 master 维护并分发)得知“某分片的主/副在谁那里”。

2.4 一致性与可用性参数

  • wait_for_active_shards:控制写入前需要有多少份分片处于 active 才返回;

    • 1(默认)仅等主分片;all 等主 + 所有副本。
  • 副本数 number_of_replicas:副本越多,读扩展强、容错高,写入成本也越高。

  • 失败与恢复:主分片宕机会由某个副本提升为新的主分片;通过序列号/主 term 等元数据保证顺序与幂等。


3. Segment 细节:为什么会有重复 term?查询怎么处理?

  • 每次 refresh 都会产出一个独立的小 segment;不同 segment 彼此独立,相同 term 会重复存在
  • 查询阶段:Lucene 会同时在多个 segment 里查同一 term 的 posting list,并把结果归并
  • merge 阶段:把多段合成大段,合并相同 term 的 posting,并物理清理删除标记(tombstones)。

这也是为什么 segment 过多会拖慢查询,merge 能提速但会消耗资源。


4. 字段与索引结构:不是所有字段都是倒排表

  • text:分词,建立倒排索引(term→posting)。
  • keyword:不分词,整个值作为一个 term 建倒排(适合精确匹配、聚合)。
  • 数值/日期/地理:不是倒排,使用 BKD 树/空间索引 支持范围/地理查询。
  • index: false:不建索引,只存储,无法被检索。
  • 多字段(multi-fields):一个字段既是 text 又是 keyword,兼顾全文与精确匹配。

5. 可观测与常用调优

5.1 可见性相关参数

  • index.refresh_interval:默认 1s;批量写入建议临时设为 -1,完毕后再改回。
  • index.translog.durabilityrequest(默认,安全) / async(快,可能丢最近一个窗口)。
  • index.translog.sync_intervalasync 模式下 fsync 周期,默认 5s

5.2 写入吞吐相关手段

  • Bulk 批量写:合并网络/解析开销;控制合理批大小(几 MB~几十 MB)。

  • 副本与刷新策略

    • 导入期可把副本数临时设为 0,导入完成再恢复。
    • 刷新间隔设为 -1,导入完成手动 _refresh
  • 映射与字段控制:禁用不需要的索引/存储/来源字段(如关闭 _all、合理控制 doc_values)。

  • Merge 影响:高写入期可以调节合并限速(避免打爆 IO),导入后允许合并充分进行以稳定查询性能。

5.3 故障与恢复

  • 宕机恢复:依赖 translog 回放 + 上次 flush 的 commit point。
  • 副本恢复:主分片把缺失的 segment/操作日志同步给副本,直到达到一致。

6. 协调节点到底值不值得?(设计权衡)

  • 好处:隐藏路由、承接集群动态、均衡负载、简化客户端。
  • 代价:多一跳转发(但通常可忽略),以及所有节点需要持有最新 Cluster State(大集群可能膨胀,需要控制索引/映射规模)。

7. 一些面试点

Q1:为什么 ES 写入快、搜索也快?
A:倒排索引/列存结构 + 分片并行 + OS cache + 针对数值/地理的 BKD/空间索引;查询时多段归并,merge 降段数。

Q2:写入成功是不是所有分片都写了?
A:不是。一条文档只落到一个主分片及其所有副本。

Q3:写入成功后为什么搜索不到?
A:还没 refresh(默认 1s)。可手动 _refresh,或等下一次 refresh。GET-by-id 默认可见(realtime)。

Q4:translog 是不是“写盘”了?还会丢?
A:写入 translog 后若没 fsync,只在 OS page cache,机器掉电可能丢。durability=request 会在返回前 fsync,安全;async 依赖 sync_interval,窗口内可能丢。

Q5:merge 什么时候发生?会影响性能吗?
A:后台自动按策略触发;会吃 IO/CPU,写入高峰要限速/合理配置,导入完成后让它合并以稳定查询时延。

Q6:为什么需要协调节点?
A:简化客户端、适应分片迁移/升主、均衡负载;谁接到请求谁协调。


8. 小结

单机:写 → buffer + translog → refresh(可搜) → flush(持久化) → merge(优化)。
集群:任一节点临时协调 → 定位主分片 → 写主并同步副本 → 返回。
可靠性durability=request 安全;async + sync_interval 快但可能丢最近窗口。
设计哲学:用不可变 segment + 异步合并,换取简单、稳定、可伸缩。

http://www.lryc.cn/news/626663.html

相关文章:

  • [系统架构设计师]面向服务架构设计理论与实践(十五)
  • [element-plus] el-tree 拖拽到其他地方,不拖拽到树上
  • Vue3 element ui 给表格的列设置背景颜色
  • 晨控EtherCAT设备分配IP操作手册
  • LWIP的TCP协议
  • 在 Golang 中复用 HTTP 连接
  • 26_基于深度学习的茶叶等级检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • CTFshow系列——命令执行web38-40
  • Qt音乐播放器项目实践:本地持久化与边角问题处理
  • 小红书账号隔离:解决IP关联问题方案
  • 网络工程师考试重点:OSI七层模型TCP/IP四层模型解析
  • 【北京迅为】iTOP-4412精英版使用手册-第三十二章 网络通信-TCP套字节
  • yolo_RK3588系列(三)
  • 5.4 4pnpm 使用介绍
  • FreeRTOS---进阶知识1---列表的创建
  • SQL 中大于小于号的表示方法总结
  • Claude Code NPM 包发布命令
  • 内网安全——出网协议端口探测
  • Java开源工具Apache PDFBox(强大的处理 PDF文档工具:创建、读取、修改、解析和提取 PDF)
  • Apache ShenYu和Nacos之间的通信原理
  • 【Tech Arch】Apache Pig大数据处理的高效利器
  • Spring Boot 日志体系详解:配置与实战
  • 三、k8s 1.29 之 资源清单
  • 网络编程5(HTTPS)
  • 【考研408数据结构-08】 图论基础:存储结构与遍历算法
  • Linux的奇妙冒险——进程pcb第二讲
  • 云原生俱乐部-k8s知识点归纳(5)
  • SpringTask入门
  • 关于多个el-input的自动聚焦,每输入完一个el-input,自动聚焦到下一个
  • Rust并发编程:解锁高性能系统的密钥