外观
时序建模通则
控制库有 6 张降采样存档表与 3 张差分基准表,分属四个子系统,但形态完全一致。本页描述这套共同形态,具体列定义见各子系统册。
| 存档表 | 桶长 | 册 |
|---|---|---|
node_iface_traffic_rollup | 5 min | 流量 |
node_traffic_rollup | 5 min | 流量 |
node_metrics_rollup | 5 min | 健康与自观测 |
autopeer_route_rollup | 5 min | 自动对等 |
prefix_flap_rate_rollup | 60 s | Flap |
flap_activity_rollup | 60 s | Flap |
统一形态
主键 = (维度…, bucket_start) bucket_start = epoch 秒,对齐桶长
值列 = <指标>_sum + sample_count 读取时以 sum / count 得均值从一次上报到一条曲线,数据流经四个环节:
四条共同纪律:
- 桶起点用
BigInteger存 epoch 秒,不使用时间戳类型:分区序号可由整除直接算出,且与时区无关。 sample_count并非冗余。 它区分「该桶值为 0」与「该桶无上报」,读侧据此绘制断档而非零值。- 累计量必须先差分再入桶。 agent 上报的是自进程启动以来的累计值,落桶前需与持久基准求差。
- 主键已包含
bucket_start,满足 PostgreSQL「分区键必须包含在唯一约束内」的硬性要求,分区化无需改动主键。
唯一的例外:gauge 语义
autopeer_route_rollup 存储的是路由条数,属于 gauge[1]:同一桶内多次观测取最后一次,既不累加也不求平均。将 gauge 按 counter 累加是时序建模中最常见的错误,该表以「不设 _sum 列」从结构上排除了这种误用。
三张差分基准表
node_traffic_last_sample、node_iface_traffic_last、node_metrics_last 在形态上属于 L2 当下值(覆盖式 upsert,丢失后下一轮重建),在用途上服务于 L3。
其存在理由是持久性:差分所需的上一份样本原本可仅存于缓存,但 Redis 可丢失,一旦丢失即出现「重启后首个桶无速率」的空洞。存入 PostgreSQL 后,Redis 不可用期间 5 分钟粒度的存档仍可正常累加。
保留与裁剪
| 表 | 保留 | 裁剪方式 | 分区目标 |
|---|---|---|---|
node_traffic_rollup | 17 280 桶 ≈ 60 天 | 按节点保留最近 N 桶(notin_ 子查询) | 是 |
node_iface_traffic_rollup | 60 天 | 按时间 cutoff DELETE | 是 |
prefix_flap_rate_rollup | 86 400 桶 = 60 天 | 按时间 cutoff DELETE | 是 |
node_metrics_rollup | 17 280 桶 ≈ 60 天 | 按时间 cutoff DELETE | 否 |
autopeer_route_rollup | 17 280 桶 ≈ 60 天 | 按时间 cutoff DELETE | 否 |
flap_activity_rollup | 86 400 桶 = 60 天 | 按时间 cutoff DELETE | 否 |
60 天有明确依据:界面最长的 range 为 30 天,而「与上期对比」需同时读取紧邻的上一个 30 天窗口,保留期必须覆盖两个最长窗口。
注意: 三张分区目标表的按行 DELETE 由 [database] partitioned_timeseries 开关门控。置真后裁剪全部交由分区维护循环 DROP 整个分区完成,因此必须在转换实际完成之后才置真——未分区却置真等同于旧行永不回收。转换步骤见迁移与演进。
按行 DELETE 的代价
随机 DELETE 持续产生死元组与索引膨胀,依赖 autovacuum 回收。实测中仅 node_traffic_rollup 的死元组稳定偏高(约 7 900),因为它同时满足两个条件:高频 upsert(一个 5 min 桶被 30 s 采样刷新约 10 次)与按行裁剪。
分区化的收益存在边界:它只消除裁剪产生的死元组,不消除 upsert 产生的。该表两个来源皆有,分区化仅解决其中一半。量化见性能与容量。
gauge 与 counter:时序指标的两种语义。counter 单调递增,关心的是增量,可跨桶累加(如累计字节数);gauge 表示某一时刻的瞬时值,只能取样或求平均,累加无意义(如路由条数、内存占用)。 ↩︎