版本升级与迁移:滚动升级、reindex 重建与回滚预案

系统讲解 Elasticsearch 版本升级与数据迁移:升级前评估与兼容性检查、滚动升级顺序与分片恢复、reindex 与远程 _reindex 重建、破坏性变更处理、回滚预案与灰度验证。

Elasticsearch 的版本迭代很快,每个大版本都会带来性能改进、新特性与若干破坏性变更。升级不只是「换个二进制重启」,它牵涉映射兼容性、查询行为变化、插件与客户端版本、数据重建成本,以及出问题时能不能退回去。升级失败往往不是技术不可行,而是准备不足——没有兼容性检查、没有回滚预案、没有灰度验证。本文按「评估 → 升级 → 恢复 → 重建 → 兼容 → 回滚 → 验证」的完整流程展开,把升级做成可计划、可验证、可回退的工程动作。

1. 升级前的评估与准备

一句话总结: 升级前的准备工作决定了升级的成败,重点是兼容性检查、版本路径确认、备份与客户端对齐。

1.1 版本升级路径

Elasticsearch 不支持跨大版本跳升,必须逐个大版本升级:7.x → 8.x 可以,6.x → 8.x 不行,要先升到 7.x。同一大版本内的小版本可以跨升,但建议逐个小版本升,便于定位问题。升级前务必查官方升级文档确认允许的路径。

1.2 兼容性检查

官方提供升级助手(Upgrade Assistant)与 _migration/deprecations 接口,能列出当前集群中在新版本会失效的配置与用法:

GET /_migration/deprecations

返回分三档:critical(阻断升级)、warning(需处理)、info(提示)。必须在升级前清零 critical 项。

1.3 客户端与插件对齐

  • 客户端:Java 高级客户端、各语言客户端要与服务端版本兼容,升级前先升级客户端或确认兼容矩阵。
  • 插件:第三方插件(如 IK 分词、analysis 插件)必须有对应版本,否则节点无法启动。
  • 周边组件:Kibana、Logstash、Beats 通常要求与服务端版本一致,需一起规划升级。

1.4 备份

升级前必须做快照,且验证快照可恢复:

PUT /_snapshot/backup-repo/pre-upgrade-snapshot?wait_for_completion=true
{
  "indices": "*",
  "include_global_state": true
}

快照是回滚的最后一道防线。没有可用快照就升级,等于赌运气。

1.5 资源与时间窗口

升级过程会有分片恢复与段合并的额外负载,磁盘要预留空间,时间窗口要避开业务高峰。滚动升级通常以小时计,大集群可能需要一整天,要提前通知业务方。

2. 滚动升级顺序与步骤

一句话总结: 滚动升级逐节点进行,先升非主节点再升主节点,保证集群始终有足够节点维持服务。

2.1 滚动升级原则

一次只停一个节点,升级并重启,等它重新加入集群、分片恢复完成,再处理下一个。这样集群始终保持多数节点在线,服务不中断。前提是每个分片至少有一个副本在未升级的节点上。

2.2 升级顺序

推荐顺序:先升专用主节点(或最后升,取决于策略),再升数据节点,最后升协调节点。关键约束是「主节点集群要能维持多数派」,通常先升级部分主节点、保留多数在线,再滚动数据节点。

实际常用顺序:关闭分片分配 → 升级所有主节点 → 升级数据节点(逐个)→ 升级协调节点 → 开启分片分配。

2.3 关闭分片自动分配

升级期间禁止分片自动重分配,避免节点重启引发不必要的分片搬移:

PUT /_cluster/settings
{
  "persistent": { "cluster.routing.allocation.enable": "primaries" }
}

升级完成后恢复为 all。

2.4 单节点升级步骤

  1. 停止节点前先同步刷盘:POST /_flush/synced(7.x+ 可用,减少恢复时间)。
  2. 停止节点进程。
  3. 安装新版本,保留配置与数据目录。
  4. 启动节点,观察日志确认加入集群。
  5. 等待分片恢复完成,检查集群健康。

2.5 版本兼容窗口

滚动升级期间集群是「混合版本」状态,ES 支持在升级窗口内混合运行,但只限相邻版本。混合期不要做映射变更或创建新索引,避免新版本特性在旧节点上无法处理。

2.6 升级后的收尾

全部节点升级完成后:恢复 cluster.routing.allocation.enable,检查集群健康转绿,验证索引与查询,观察一段时间的性能指标。若使用 ILM,确认策略在新版本下正常执行。

3. 集群重启与分片恢复

一句话总结: 全集群重启(停机升级)要按「先停后启、主节点先行」的顺序,并利用分片恢复优先级与并发控制加速。

3.1 何时需要全集群重启

当无法滚动升级(例如跨大版本且索引不兼容、或需要变更节点角色配置)时,采用停机升级:停全部节点,升级,再全部启动。代价是服务中断。

3.2 停机升级顺序

  1. 停止索引写入,POST /_flush/synced。
  2. 停止所有数据节点与协调节点。
  3. 停止主节点(最后停)。
  4. 升级全部节点软件。
  5. 先启动主节点(等其选出 leader),再启动数据节点。
  6. 等待分片恢复。

3.3 加速分片恢复

集群重启后大量分片同时恢复会争抢 IO。可以调节恢复并发:

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.node_concurrent_recoveries": 4,
    "indices.recovery.max_bytes_per_sec": "200mb"
  }
}

node_concurrent_recoveries 控制单节点并发恢复数,max_bytes_per_sec 限制恢复带宽。恢复期间业务查询会变慢,可在恢复完成后调回。

3.4 恢复优先级

用索引优先级让重要索引先恢复:

PUT /critical-index/_settings
{
  "index.priority": 100
}

数值越大越先恢复。ILM 的 set_priority 也可用于此。把核心业务索引设为高优先级,非核心的日志索引靠后。

3.5 恢复状态观测

GET /_cat/recovery?v&active_only=true
GET /_cluster/health?wait_for_status=yellow&timeout=5m

_cat/recovery 显示每个分片的恢复进度与来源(本地还是远程)。本地恢复(同节点数据仍在)远快于远程恢复。

3.6 恢复失败的排查

恢复卡住常见原因:磁盘水位超限导致分片无法分配、节点角色变更后分片无处可去、数据目录权限错误。用 GET /_cluster/allocation/explain 查看具体原因。

4. reindex 与 _reindex 远程重建

一句话总结: reindex 把文档从一个索引复制到另一个,用于映射变更、跨集群迁移与数据清洗,代价是重建期间的双份存储与耗时。

4.1 为什么需要 reindex

Elasticsearch 的映射大多不可修改(如字段类型、分词器)。要改这类映射,只能建新索引、reindex 数据、切别名。跨大版本迁移也常用 reindex 重建索引结构。

4.2 本地 reindex

POST /_reindex?wait_for_completion=false
{
  "source": { "index": "logs-v1" },
  "dest": { "index": "logs-v2" }
}

wait_for_completion=false 返回任务 ID,用 GET /_tasks/<id> 查询进度,避免长连接超时。

4.3 带查询与脚本的 reindex

只迁移部分数据或做字段变换:

POST /_reindex
{
  "source": {
    "index": "logs-v1",
    "query": { "range": { "@timestamp": { "gte": "2026-01-01" } } }
  },
  "dest": { "index": "logs-v2" },
  "script": {
    "source": "ctx._source.level = ctx._source.remove('severity')"
  }
}

脚本可重命名字段、做类型转换、补充新字段,是映射演进时的常用手段。

4.4 远程 reindex

从一个集群迁移到另一个集群:

POST /_reindex
{
  "source": {
    "remote": {
      "host": "https://old-cluster:9200",
      "username": "migrator",
      "password": "secret"
    },
    "index": "logs-v1",
    "size": 1000
  },
  "dest": { "index": "logs-v2" }
}

需要在目标集群的 reindex.remote.whitelist 中放行源集群地址。远程 reindex 走 HTTP,速度受网络与源集群负载限制。

4.5 加速 reindex

  • 关闭刷新与副本:目标索引先设 refresh_interval: -1、number_of_replicas: 0,完成后恢复。
  • 调整批次:size 控制每批文档数,过大占用内存,过小协议开销高,常用 1000~5000。
  • 并行切片:用 slices: auto 让 reindex 并行:
POST /_reindex?slices=auto&wait_for_completion=false
{
  "source": { "index": "logs-v1" },
  "dest": { "index": "logs-v2" }
}

4.6 版本升级场景的 reindex

跨大版本时,通常先在旧集群把数据 reindex 到新集群(新集群建好新版本索引),或用快照恢复到新集群再升级。两条路径各有取舍:reindex 灵活但慢,快照快但要求版本兼容。

4.7 别名原子切换

reindex 完成后,把读写别名指向新索引:

POST /_aliases
{
  "actions": [
    { "remove": { "index": "logs-v1", "alias": "logs" } },
    { "add": { "index": "logs-v2", "alias": "logs", "is_write_index": true } }
  ]
}

别名切换是原子的,应用无感知。切换后旧索引先保留一段时间做回退兜底,确认无误再删除。

4.8 reindex 的坑

  • 双份存储:新旧索引同时存在,磁盘要有足够空间。
  • 版本字段冲突:源文档带 _version 或 _id 冲突时的处理策略要明确。
  • 任务中断:reindex 中断后可以重跑,但已写入的文档会因 _id 相同而覆盖,需确认幂等。
  • 耗时不可控:大索引 reindex 可能数小时到数天,务必用异步任务并监控进度。

5. 破坏性变更与映射兼容

一句话总结: 每个大版本都有破坏性变更,重点是 REST API 路径、映射参数、默认行为与查询语法的变化。

5.1 常见破坏性变更类型

  • API 路径变更:如类型(type)在 7.x 移除、_search 部分参数废弃。
  • 映射参数移除:如 string 类型早已拆分为 text 与 keyword,旧参数陆续废弃。
  • 默认行为变化:默认分片数、refresh 间隔、total_fields.limit 等默认值调整。
  • 查询语法收紧:宽松的语法被拒绝,如数字与字符串的隐式转换。
  • 安全默认开启:8.x 默认启用安全,升级后需配置 TLS 与认证,否则节点无法组网。

5.2 映射兼容性

大版本间映射通常向后兼容(旧索引在新版本可读),但少数类型或参数被移除后需要 reindex。升级助手的 critical 项会指出必须处理的索引。

GET /_migration/deprecations

对提示需要重建的索引,提前规划 reindex。

5.3 安全配置的迁移

8.x 默认开启安全,升级到 8.x 必须:

  • 配置 TLS 证书(传输层与 HTTP 层)。
  • 设置内置用户密码。
  • 更新客户端连接配置(增加认证与 CA 证书)。

这是 7 → 8 升级最常见的阻断点,必须在预发环境完整演练。

5.4 客户端 API 变更

客户端库的 API 在大版本间常有破坏性调整,例如请求体构造方式、响应解析结构。升级服务端前先升级并测试客户端,避免线上服务在升级后无法连接。

5.5 逐步收紧兼容层

部分旧语法在新版本仍可通过兼容参数使用一段时间,但会在后续版本移除。升级时不要依赖兼容层,应直接迁移到新语法,避免下次升级重复踩坑。

5.6 插件与分词器

IK 等中文分词插件必须匹配版本。升级前确认插件有新版本,并在预发环境验证分词结果一致——分词行为变化会直接影响搜索相关性。

6. 回滚预案与灰度验证

一句话总结: 升级前先想好怎么退回去,灰度验证用少量流量试水,两者共同把升级风险控制在可接受范围。

6.1 回滚的三个层次

  • 快照回滚:数据损坏时从升级前快照恢复,最彻底但耗时最长。
  • 版本回滚:停掉新版本、装回旧版本重启,要求数据格式仍兼容旧版本(大版本回滚通常不可行)。
  • 别名回滚:reindex 场景下把别名切回旧索引,秒级完成。

大版本升级通常无法原地回滚(新版本写入的数据旧版本读不了),所以真正的回滚依赖快照与双集群并行。

6.2 双集群并行方案

更安全的做法是「新建新版本集群 + 数据迁移 + 灰度切流 + 旧集群保留」。切流异常时把流量切回旧集群,秒级回滚。代价是双份资源,但风险最低,适合核心业务。

6.3 灰度验证

  • 功能验证:核心查询、聚合、写入、ILM 在新版本结果一致。
  • 性能验证:对比升级前后的查询延迟与吞吐,确认无退化。
  • 兼容验证:客户端、Kibana、Logstash、Beats 全部连通。
  • 数据验证:抽样对比文档数与查询结果。

6.4 分阶段切流

先把只读查询切到新集群(如 1% → 10% → 50% → 100%),观察指标;再切写入。写入切换是不可逆的关键点,切换前必须确认新集群已具备全部数据。

6.5 回滚触发条件

预先定义回滚判据:错误率超过阈值、延迟劣化超过阈值、数据不一致、关键功能不可用。判据要可量化、可自动检测,避免临场犹豫。

6.6 升级窗口与沟通

明确升级时间窗口、影响范围、联系人,做好业务方沟通。核心业务尽量安排在低峰期,并预留回滚所需的额外时间。

7. 升级后的验证与调优

一句话总结: 升级不是终点,要持续观察性能、验证功能,并利用新版本特性做进一步优化。

7.1 健康与性能基线

升级后重新采集性能基线:查询延迟分布、写入吞吐、段合并频率、GC 情况。与升级前对比,若出现退化要定位原因——可能是默认参数变化,也可能是新版本的资源模型不同。

7.2 参数复核

新版本可能调整了默认值(如分片数、refresh 间隔、线程池大小),显式配置的参数也要复核是否仍适用。例如 total_fields.limit 的默认值变化会影响字段多的索引。

7.3 利用新特性

每个大版本都带来可观的性能改进(如新的执行引擎、更快的聚合、ES|QL)。升级后评估是否启用新特性来获得收益,例如时序场景启用 TSDB 模式、分析场景试用 ES|QL。

7.4 清理与回收

升级后清理:删除旧的兼容配置、移除已废弃的参数、删除不再需要的旧索引与快照、回收临时扩容的资源。

7.5 文档与知识沉淀

把升级过程中遇到的坑、验证步骤、回滚流程记录下来,形成可复用的升级手册。下一次升级时,这份手册能省下大量时间。

7.6 持续监控

升级后一段时间内保持更密集的监控,关注错误率、延迟、集群健康与磁盘。很多问题在升级后数小时甚至数天才显现(如 ILM 策略在新版本下的行为变化)。

8. 总结

环节要点
升级路径逐个大版本,不可跨版本跳升
兼容检查_migration/deprecations 清零 critical
备份升级前快照并验证可恢复
滚动升级逐节点进行,关闭分片自动分配
停机升级主节点先行启动,调恢复并发加速
reindex建新索引 + 切片并行 + 别名原子切换
破坏性变更API 路径、映射参数、安全默认开启
回滚快照回滚、版本回滚、别名回滚三层
灰度验证分阶段切流,预设可量化回滚判据

升级与迁移的复杂度不在技术本身,而在「不可逆」这三个字。把兼容性检查、快照备份、灰度切流、回滚预案全部前置,升级就从一场冒险变成一次例行操作。映射变更与 reindex 的细节见《数据建模与 Mapping 设计》,备份恢复见《部署运维与备份恢复》,集群健康与滚动重启见《集群运维与监控》,而新版本引入的 ILM 与索引治理能力见《索引生命周期与滚动》。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「elasticsearch」更多文章

  1. 可搜索快照与冻结层:把冷数据放进对象存储还能查
  2. 分页与深度分页:from/size、search_after、PIT 与 scroll
  3. 嵌套与父子关联查询:nested、join 字段与性能取舍