📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 6.8 - 8.12

1. 错误说明 #

failed to create local checkpoint tracker 表示 Elasticsearch 在分片引擎启动阶段,尝试构建本地 checkpoint 跟踪器(LocalCheckpointTracker)时失败。该跟踪器负责维护分片级别已完成的序列号(sequence number)状态,是写入和恢复流程中的核心数据结构。

当引擎初始化过程中读取 segments_N 提交点元数据、解析 user data 或重建序列号完成状态时,如果发生 IOException,就会抛出 EngineCreationFailureException,并携带 failed to create local checkpoint tracker 信息。

常见现象 #

  • 分片无法正常启动,yellowred 状态持续存在。
  • 节点日志中出现 EngineCreationFailureException,伴随 failed to create local checkpoint tracker
  • 受影响的索引部分分片处于 UNASSIGNED 或反复 INITIALIZING 后失败。
  • 集群可能触发分片重分配,但新分配的分片同样无法启动,形成恶性循环。
  • 节点启动、索引恢复或段合并过程中更容易触发此异常。

典型报错与异常栈 #

org.elasticsearch.index.engine.EngineCreationFailureException: failed to create local checkpoint tracker
Caused by: java.io.IOException: failed to read segment info
    at org.elasticsearch.index.engine.InternalEngine.createLocalCheckpointTracker(InternalEngine.java:XXX)
    at org.elasticsearch.index.engine.InternalEngine.<init>(InternalEngine.java:XXX)
Caused by: java.io.EOFException
    at org.apache.lucene.store.DataInput.readVInt(DataInput.java:XXX)

2. 原因分析 #

本地 checkpoint 跟踪器依赖 Lucene 提交点元数据和序列号状态来恢复内部数据结构,因此任何导致提交点无法正确读取的问题都会触发此异常。常见原因包括:

  • 提交点文件损坏segments_N 文件写入不完整、被截断或在节点异常退出时损坏,导致无法解析段信息。
  • 磁盘 I/O 异常:磁盘坏道、文件系统错误、磁盘空间耗尽或 I/O 超时,使读取 segments_N.si 段信息文件失败。
  • user data 解析失败:Elasticsearch 在 segments_N 的 user data 中存储序列号、历史版本等元数据,若内容损坏或格式异常,解析时会抛出异常。
  • 分片数据不一致:分片副本之间数据不一致,或者 translog 与段文件状态不匹配,导致恢复流程无法重建 checkpoint 状态。
  • 节点异常崩溃后的残留状态:节点在写入提交点过程中被强制终止(如 OOM kill、断电),导致提交点处于中间状态。
  • Lucene 版本不兼容:跨大版本升级后,旧版 Lucene 写入的段格式与新版不兼容,读取时出错。

3. 解决方案 #

3.1 快速确认影响范围 #

# 查看集群健康状态,确认受影响的索引和分片
curl -s "http://localhost:9200/_cluster/health?pretty"

# 查看 UNASSIGNED 分片详情
curl -s "http://localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason&pretty"

# 查看具体节点的异常日志
grep -r "failed to create local checkpoint tracker" /var/log/elasticsearch/

3.2 检查磁盘和文件系统 #

# 检查磁盘空间
df -h

# 检查磁盘 I/O 错误(dmesg)
dmesg | grep -i "error\|io\|ext4\|xfs"

# 对数据目录做文件系统一致性检查(卸载后执行)
# 以 ext4 为例:
# umount /data/elasticsearch
# fsck -f /dev/sdX

3.3 尝试从健康副本恢复 #

如果索引有其他健康副本,优先让 Elasticsearch 自动从健康副本恢复:

# 临时排除故障节点,触发分片重分配
curl -X PUT "http://localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d '{
  "transient": {
    "cluster.routing.allocation.exclude._name": "faulty-node-name"
  }
}'

# 确认分片重新分配后,再恢复设置
curl -X PUT "http://localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d '{
  "transient": {
    "cluster.routing.allocation.exclude._name": null
  }
}'

3.4 从快照恢复 #

如果本地副本均已损坏,从快照恢复是最稳妥的方案:

# 查看可用快照
curl -s "http://localhost:9200/_snapshot/_all?pretty"

# 从快照恢复指定索引
curl -X POST "http://localhost:9200/_snapshot/my_backup/snapshot_20240601/_restore" -H 'Content-Type: application/json' -d '{
  "indices": "damaged_index_name",
  "include_global_state": false
}'

3.5 清除本地损坏数据(最后手段) #

警告: 此操作会导致该分片副本数据丢失,仅在其他副本健康或可从快照恢复时执行。

# 1. 停止受影响的节点
systemctl stop elasticsearch

# 2. 备份并删除当前分片数据目录
# 路径通常为:/path/to/data/nodes/0/indices/<index_uuid>/<shard_id>/
mv /path/to/data/nodes/0/indices/<index_uuid>/0 /tmp/shard_0_backup

# 3. 启动节点,分片将从其他副本自动恢复
systemctl start elasticsearch

4. 预防措施 #

  • 保障磁盘健康:定期监控磁盘 SMART 信息、I/O 延迟和错误计数,及时更换异常磁盘。
  • 预留充足磁盘空间:确保数据目录所在分区始终保留至少 15% 的可用空间,避免写入过程中空间耗尽。
  • 使用软关机流程:避免直接 kill -9 或断电,使用 systemctl stop elasticsearch 等优雅停止方式。
  • 定期快照:为关键索引配置定期快照策略,确保数据损坏时有可靠恢复源。
  • 控制分片大小:过大的分片在恢复时更容易超时或出错,建议单个分片大小控制在 10GB-50GB 以内。
  • 监控分片状态:通过 INFINI Console 等工具持续监控分片健康状态,在 UNASSIGNED 出现初期及时介入。

5. 小结 #

failed to create local checkpoint tracker 本质上是分片引擎在恢复本地序列号状态时失败,根因几乎总是指向提交点元数据损坏或磁盘读取异常。处理时应优先利用健康副本或快照恢复,避免直接强行重启或删除提交点文件。只有在确认数据可以从其他来源恢复的前提下,才考虑清除本地损坏数据进行重建。

对于生产环境,建立完善的快照策略和磁盘监控体系,是避免此类问题造成业务中断的最有效手段。

相关错误 #

附:日志上下文 #

tracker::markSeqNoAsCompleted);
}
}
return tracker;
} catch (IOException ex) {
    throw new EngineCreationFailureException(engineConfig.getShardId(), "failed to create local checkpoint tracker", ex);
}
}  private SoftDeletesPolicy newSoftDeletesPolicy() throws IOException {
    final Map<String, String> commitUserData = store.readLastCommittedSegmentsInfo().userData;