ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SkyWalking 升级 7.x 后 Hour/Day 精度指标索引停止更新:原因分析与清理方案

SkyWalking 升级 7.x 后 Hour/Day 精度指标索引停止更新:原因分析与清理方案 SkyWalking 升级 7.x 后 Hour/Day 精度指标索引停止更新原因分析与清理方案【免费下载链接】skywalkingAPM, Application Performance Monitoring System项目地址: https://gitcode.com/gh_mirrors/sky/skywalking本文聚焦 Apache SkyWalking OAP 从 6.x 升级到 7.x 后Elasticsearch 存储中*-hour_xxxxx与*-day_xxxxx索引不再更新的常见问题。文章从官方 FAQ 出发结合 7.0.0 变更记录、Elasticsearch 存储插件源码与索引命名实现解释该现象是“Downsampling Data Packing降采样数据打包”特性生效后的预期行为并给出删除过期索引、核对当前索引模型、调整dayStep与 TTL 的完整实战方案。读完本文你将能够判断升级后的索引差异是否正常并安全地完成历史索引清理与存储配置调优。问题现象升级后 Hour/Day 索引停止写入在将 SkyWalking OAP 从 6.x 升级到 7.x 后部分用户发现 Elasticsearch 中带有hour与day时间精度的指标索引不再有新数据写入表现为service_instance_xxx-day_20260919这类日精度索引的文档数长时间不再增长service_xxx-hour_20260919这类小时精度索引同样停止更新只有形如service_instance_xxx-20260919分钟精度无精度关键词与service_instance_xxx-month_202609的索引仍在持续写入。这并非故障而是 7.x 存储实现变化导致的预期行为。官方在 Hour-Day-Metrics-Stopping.md 中明确说明该问题源于 Elasticsearch 存储的Downsampling Data Packing降采样数据打包特性。建议先阅读 Elasticsearch 存储配置文档 了解索引相关配置再结合本文处理索引变更。根因7.x 将 Hour/Day 降采样数据打包进分钟索引7.0.0 变更记录中的关键一行在 changes-7.0.0.md 的 OAP-Backend 章节有一条与本问题直接相关的变更Merge the HOUR and DAY metrics into MINUTE in the ElasticSearch storage implementation. Reduce the payload for ElasticSearch server.即从 7.0.0 开始SkyWalking OAP 在 Elasticsearch 存储实现中不再为 HOUR 与 DAY 精度的指标单独创建独立索引而是将它们统一合并写入 MINUTE分钟精度的索引中。官方 FAQ 也印证了这一点Currently, SkyWalking uses themetrics name-xxxxxandmetrics name-month_xxxxxindexes only.其中metrics name-xxxxx对应分钟精度索引xxxxx为日期时间戳metrics name-month_xxxxx对应月份精度索引。也就是说升级后实际使用的只有这两类索引。降低存储开销的设计意图该变更是为了减少 Elasticsearch 集群的存储与 IO 压力此前 HOUR/DAY 降采样数据各自维护独立索引会产生大量冗余副本合并进分钟索引后同一份数据即可支撑秒/分钟/小时/日多个查询精度不再需要为高层级精度重复落盘。这一设计也与 7.0.0 同期引入的Daily Index Step每日索引步长配合见 changes-7.0.0.md 中 Support Daily step in the ElasticSearch storage implementation for low traffic system 一行共同构成 7.x 以降采样打包 按日滚动索引为核心的存储策略。索引命名模型从源码看 7.x 之后如何组织索引要确认“当前到底使用哪些索引”可以从 Elasticsearch 存储插件的索引名生成逻辑入手。核心类为 TimeSeriesUtils.java其中writeIndexName(Model model, long timeBucket)根据模型的降采样级别DownSampling决定索引后缀static String writeIndexName(Model model, long timeBucket) { String tableName IndexController.INSTANCE.getTableName(model); if (model.isRecord() model.isSuperDataset()) { // 记录型超大数据集如 trace segment走 superDatasetDayStep return tableName Const.LINE compressTimeBucket(timeBucket / 1000000, SUPER_DATASET_DAY_STEP); } else { switch (model.getDownsampling()) { case None: return tableName; // 非时序数据无时间后缀 case Hour: return tableName Const.LINE compressTimeBucket(timeBucket / 100, DAY_STEP); case Minute: return tableName Const.LINE compressTimeBucket(timeBucket / 10000, DAY_STEP); case Day: return tableName Const.LINE compressTimeBucket(timeBucket, DAY_STEP); case Second: return tableName Const.LINE compressTimeBucket(timeBucket / 1000000, DAY_STEP); default: throw new UnexpectedException(Unexpected down sampling value, model.getDownsampling()); } } }从源码结构可以推断出索引命名的换算逻辑分钟Minute精度时间桶除以10000后拼入索引名得到yyyyMMdd日期后缀即metrics-20260919小时Hour精度时间桶除以100后拼入索引名同样是yyyyMMdd日期后缀日Day精度时间桶直接拼入索引名仍是yyyyMMdd日期后缀月Month精度对应metrics-month_202609形式month_前缀 yyyyMM年月后缀。关键点在于Hour、Minute、Day 三种精度最终都会落到同一个yyyyMMdd后缀的索引里。因此在 7.x 中service-20260919这一个索引同时承载了分钟、小时、日三个降采样级别的数据这正是“Hour/Day 索引停止更新”的直接原因——它们不再有独立索引了。降采样级别的枚举定义见 DownSampling.javapublic enum DownSampling { None(0, ), // 非时序数据 Second(1, second), // 用于 record、profile、top n 等明细数据 Minute(2, minute), Hour(3, hour), Day(4, day); }TTL 清理逻辑为什么旧索引不会再被写入7.x 的 TTL数据保留期删除逻辑也印证了这一打包策略。见 HistoryDeleteEsDAO.java 的deleteHistory方法if (!model.isRecord()) { if (!DownSampling.Minute.equals(model.getDownsampling())) { /* * In ElasticSearch storage, the TTL triggers the index deletion directly. * As all metrics data in different down sampling rule of one day are in the same index, the deletion operation * is only required to run once. */ return; // 非分钟精度的指标模型跳过独立 TTL 删除 } }源码注释明确说明“一天内不同降采样规则的所有指标数据都在同一个索引中删除操作只需执行一次”。因此TTL 任务只针对分钟精度模型执行删除因为分钟索引已包含 Hour/Day 数据删除时按索引名中的日期时间戳与deadline now - ttl比较过期即删见 HistoryDeleteEsDAO.java 中isolateTimeFromIndexName与deleteByIndexName的调用indexLatestSuccess缓存避免了同一批索引被重复扫描删除。所以那些遗留的*-day_xxxxx、*-hour_xxxxx索引不会被 TTL 任务管理它们属于 6.x 时代创建的旧索引需要手动处理。处理方案安全删除过期的 Hour/Day 索引官方 FAQ 给出的建议非常直接You may simply delete all expired*-day_xxxxxand*-hour_xxxxx(xxxxxis a timestamp) indexes.即删除所有已经过期的*-day_xxxxx与*-hour_xxxxx索引xxxxx为时间戳如20260919。操作步骤与注意事项如下。1. 确认当前生效的索引模型删除前先通过 Elasticsearch 的_cat/indices接口列出所有索引确认升级后仍在写入的索引集合# 查看所有 SkyWalking 相关索引可按 namespace 前缀过滤默认 sw_ curl -s http://localhost:9200/_cat/indices/sw_*?vsindex正常情况下7.x 集群中指标索引应呈现为sw_service-20260919分钟精度同时承载 Hour/Day 数据sw_service-month_202609月精度记录型索引如sw_segment-20260919、sw_log-20260919等。而那些sw_service-day_20260919、sw_service-hour_20260919形态的索引即为 6.x 遗留、升级后不再写入的旧索引。2. 按时间筛选并删除过期索引结合_cat/indices的creation.date或索引名中的时间戳筛选出超过 TTL 保留期的旧索引后删除# 例删除命名符合 *-day_* 的索引请先替换为实际的保留期过滤条件 curl -s -X DELETE http://localhost:9200/sw_service-day_20260819,sw_service-hour_20260819也可以借助索引别名批量管理。7.x 的 TTL 删除通过retrievalIndexByAliases(tableName)拿到索引集合后逐个删除见 HistoryDeleteEsDAO.java手动清理时同样建议先确认索引名再执行删除。务必先备份或确认索引数据已无保留价值。如果这些旧索引中还有需要继续查询的历史数据应先通过 reindex 迁移到新索引或延长保留期后再清理避免误删。3. 关于xxxxx时间戳的说明FAQ 中的xxxxx即索引名中的时间戳例如sw_service_instance-day_20260919中的20260919表示该索引覆盖 2026-09-19 当天的日精度数据sw_service-hour_20260919中的20260919表示该索引覆盖 2026-09-19 当天的小时精度数据。清理时只需依据这个时间戳判断是否已超出 TTL 即可。进阶dayStep 与 TTL 的联动配置理解 7.x 的索引打包机制后还需注意两个会影响索引行为与清理节奏的配置项它们定义在 elasticsearch.md 中storage: elasticsearch: # ...... dayStep: ${SW_STORAGE_DAY_STEP:1} # Represent the number of days in the one minute/hour/day index. superDatasetDayStep: ${SW_STORAGE_ES_SUPER_DATASET_DAY_STEP:-1} # Represent the number of days in the super size dataset record index, the default value is the same as dayStep when the value is less than 0dayStep一个索引容纳几天的数据dayStep默认 1表示一个分钟/小时/日索引覆盖的天数。文档给出的例子当dayStep 11时[2000-01-01, 2000-01-11]的数据被合并进索引index-20000101[2000-01-12, 2000-01-22]的数据被合并进索引index-20000112。对应到源码就是 TimeSeriesUtils.java 中compressTimeBucket的取模归并逻辑static long compressTimeBucket(long timeBucket, int dayStep) { if (dayStep 1) { DateTime time TIME_BUCKET_FORMATTER.parseDateTime( timeBucket); int days Days.daysBetween(DAY_ONE, time).getDays(); int groupBucketOffset days % dayStep; return Long.parseLong(time.minusDays(groupBucketOffset).toString(TIME_BUCKET_FORMATTER)); } else { return timeBucket; // dayStep1 时无需计算 } }DAY_ONE被固定为20000101见 TimeSeriesUtils.java 中的DAY_ONE常量确保无论 OAP 何时启动基于dayStep的索引分组始终一致。dayStep的典型适用场景低流量环境下希望设置很长的 TTL如超过 60 天但 ES 集群承载能力有限。此时可将dayStep调大如 5 或更多让单个索引容纳多天数据减少索引数量。配置生效路径在 StorageModuleElasticsearchProvider.javaif (config.getDayStep() 1) { TimeSeriesUtils.setDAY_STEP(config.getDayStep()); TimeSeriesUtils.setSUPER_DATASET_DAY_STEP(config.getDayStep()); } if (config.getSuperDatasetDayStep() 0) { TimeSeriesUtils.setSUPER_DATASET_DAY_STEP(config.getSuperDatasetDayStep()); }调整 dayStep 时必须同步放大 TTL官方文档特别提醒NOTE: TTL deletion would be affected by these steps. You should set an extra dayStep in your TTL. For example, if you want to have TTL 30 days and dayStep 10, you are recommended to set TTL 40.即 TTL 删除受dayStep影响设置 TTL 时需要额外加上一个dayStep的余量。例如期望保留 30 天数据、dayStep 10时建议将 TTL 设为 40。原因在于索引是按dayStep天分组的删除以整个索引为单位最后一个未满的索引组也会被整体保留或整体删除多出一个dayStep的缓冲可以避免边界数据被提前清理。总结与自查清单升级 7.x 后*-hour_xxxxx、*-day_xxxxx索引停止更新属于 7.0.0 “将 HOUR/DAY 指标合并进 MINUTE 索引”的预期行为。落地时可按下述清单自查检查项预期结果依据当前使用的指标索引仅metrics-xxxxx分钟与metrics-month_xxxxx月Hour-Day-Metrics-Stopping.md、changes-7.0.0.md6.x 遗留的*-hour_*/*-day_*索引不再写入、不再被 TTL 管理需手动删除HistoryDeleteEsDAO.java分钟/小时/日数据是否仍可查询是三者共用同一yyyyMMdd后缀索引TimeSeriesUtils.javaTTL 与dayStepTTL 应额外加上dayStep余量elasticsearch.md 中 Daily Index Step 小节一句话结论升级 7.x 后出现 Hour/Day 索引停止更新是设计使然——只需手动删除过期的*-hour_xxxxx与*-day_xxxxx旧索引若同时调整了dayStep务必同步放大 TTL并确认分钟索引已完整承载各精度数据后再清理。【免费下载链接】skywalkingAPM, Application Performance Monitoring System项目地址: https://gitcode.com/gh_mirrors/sky/skywalking创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进