聚合与降采样
默认情况下,小部件收到的是原始行:所有匹配其过滤条件的记录,最新的在前,直到达到行限制为止。对于展示最新读数的表格来说,这样正好;但对于展示最近半年的图表来说,这样完全不合适——每秒采样一次的传感器在该时间范围内会产生 1500 万行数据,而一张 600 像素宽的图表最多只能画出约 600 个点。
聚合一次性解决了这两个问题。项目数据库把匹配的记录分组到时间桶中,为每个桶计算出一个值,只把这些值发送给小部件。一年的数据变成几百个点,而原始数据无法回答的问题——每小时的平均温度、每台机器的记录数、今天的总能耗——也变成了普通的小部件配置项。
一切都发生在数据库中,在数据到达浏览器之前。小部件不做任何计算,因此无论背后积累了多少历史数据,聚合小部件都能保持实时且快速。
启用聚合
聚合与过滤器位于同一个对话框中——即数据绑定旁的过滤器图标打开的那个对话框。有关该对话框的整体说明,请参阅数据绑定。
在对话框中,聚合部分提供两个选项:
- 原始行 — 默认值:按记录存储的原样返回。
- 按时间聚合 — 记录被分组,并归约为每组一个值。
切换到按时间聚合后,会出现方法选择器,以及在适用时出现下文介绍的桶大小控件。由于任何聚合都需要一个时间范围才能工作,若小部件此前还没有时间窗口,启用聚合还会自动填入最近 24 小时的默认滚动窗口——可在正上方的滚动时间窗口部分调整。
聚合方法
| 方法 | 每个桶的结果 | 适用于 |
|---|---|---|
| 平均值 | 各值的算术平均数 | 数值列 |
| 总和 | 各值的合计 | 数值列 |
| 计数 | 含有值的记录条数 | 任意列 |
| 最小值 | 最小的值 | 数值、文本和时间戳列 |
| 最大值 | 最大的值 | 以上任意一种 |
| 第一个值 | 桶内最早那条记录的值 | 任意列 |
| 最后一个值 | 桶内最新那条记录的值 | 任意列 |
一个方法作用于同一个数据系列的全部值列。若要展示同一测量值的多项统计——例如最小值/最大值区间带加一条平均值曲线——请为每项统计各添加一个数据系列。它们共用同一次数据库查询,因此额外的系列不会带来额外的请求开销。
**平均值、总和、最小值和最大值要求列中存放的是数字。**若把它们指向文本列或布尔列,系统会拒绝并给出说明信息。计数适用于任何列,因此它正是回答”有多少条记录”这类问题的方法。
哪些内容会被聚合
您无需显式列出参与聚合的列——平台会从您已为小部件配置的字段映射中推导出来:
| 字段角色 | 聚合时的含义 |
|---|---|
x = tsp | 图表具有时间轴:时间桶对时间窗口进行细分。 |
| x = 其他任意列 | 图表具有类别轴:该列用于对记录分组。 |
| pivot | 按不同取值把结果拆分为多个系列,各自单独聚合。 |
| y、r 及其他值字段 | 使用所选方法进行聚合。 |
这意味着您用于原始行的那套映射可以继续沿用:把 x 映射到时间戳、y 映射到 temperature,打开聚合,图表就会绘制每个桶的平均温度,而不再是每一条单独的读数。
聚合小部件的三种形态
1. 自动分辨率的时间序列
最常见的情形:x 映射到 tsp,桶模式保持为自动——与小部件分辨率匹配。平台随后根据小部件的实际渲染宽度选择桶大小——一张 600 像素宽的图表,无论时间窗口多长,收到的点都不会超过约 600 个。把窗口从一天扩大到一年,桶只是变宽而已;经网络传输的数据量保持不变。
当桶大小本身并不承载含义,您只想要一张可读且快速的趋势图时,请选择这种方式:“最近一个月的温度”。
调整小部件尺寸会按新的分辨率重新取数,但只有宽度跨越某一档位时才会触发——小幅拖动不会产生请求。
2. 固定桶大小的时间序列
选择固定分桶大小并指定长度与单位——每 1 小时、每 15 分钟、每 6 小时。当这个间隔本身是问题的一部分、而不只是显示细节时,请使用这种方式:“每小时产量”、“每天能耗”。
固定桶会按您浏览器所在时区的自然边界对齐:6 小时的桶从本地时间 00:00、06:00、12:00 和 18:00 开始;按天的桶在本地零点分界;按周的桶从周一开始。
如果所选大小会产生超过图表所能合理展示的桶数(上限为 2000),平台会自动把桶放大为您所设定值的整数倍——整整一周的一分钟桶会变成五分钟桶——而不是拒绝该查询。发生这种情况时,对话框会告知您,并显示实际使用的大小。
3. 每个类别一个值
把 x 映射到某一列而非时间戳——机器名称、产品型号、站点——小部件就变成了分类汇总:整个时间窗口被视为单个桶,该列的每一个不同取值恰好产生一行聚合结果。
这是*“今天每台机器的记录数”或”本周每条产线的平均节拍时间”*所对应的形态。这里没有桶大小可选——对话框会直接说明,而不显示间隔控件。
统计记录条数:一个分组的行数就是对时间戳列 tsp 应用 计数,因为每条记录都有时间戳。把 y 映射到 tsp,平台会自动选择计数——这是时间戳列唯一允许的聚合方式。
x → machine_name (对记录分组)
y → tsp (计数 → 记录条数)
Method: Count
Window: last 1 day结果是每台机器一根柱子,显示该机器在最近一天内产生了多少条记录。
聚合单值
标量绑定——KPI 卡片的数值、仪表盘指针、文本字段——在值部分提供同样的选择:
- 最新值 — 与以往一样,取最新记录的值。
- 按时间窗口聚合 — 在整个窗口上计算出一个值。
可用于实时的关键数字:“今日产量”(对 tsp 使用 计数,窗口为 1 天)、“本班次平均温度”、“本月峰值功率”。这里同样没有桶大小——窗口本身就是那个桶。该值会随新记录到达而刷新,因此与直接写死在小部件里的数字不同,它永远不会过时。
过滤器、时间窗口与聚合的协同
对话框中各部分按其显示顺序生效,这也是项目数据库应用它们的顺序:
- 滚动时间窗口 / 自定义时间窗口 — 选定时间段。
- 过滤器 — 选定该时间段内哪些记录参与计算。
- 聚合 — 把选中的记录归约为每个桶一个值。
- 行限制 — 兜底措施,见下文。
由于过滤器在聚合之前运行,它们决定了进入每个桶的内容。machine = 'A' 与按小时求平均值组合起来,得到的就是机器 A 的每小时平均值。绑定到过滤器小部件或路由路径段的过滤器,其行为与用于原始行时完全一致,因此聚合图表也能像其他小部件一样跟随下拉菜单或页面 URL 变化。
日历小部件同样可以组合使用:当用户选定一个时间范围时,该范围会取代滚动窗口,聚合随之调整。
窗口吸附
聚合处于启用状态时,窗口的起点会吸附到桶的网格上,使得随着时间推移各个桶本身保持不动——否则每次刷新都会让所有桶边界略微偏移,整张图表就会不停抖动。对话框会以*“窗口以 … 为单位移动。“*提示这一点,而解析后的起止时间预览始终显示实际查询的范围。
窗口的终点永远不会被向后延伸:截止到现在的窗口仍然截止到现在,截止到一小时前的窗口也绝不会伸入最后这一小时。
聚合下的行限制
行限制依然可见,但其作用发生了变化:桶的数量由小部件尺寸或您设定的固定间隔推导得出,而不是由该字段决定。它只在小部件不得不回退到原始行时才生效——例如在平台版本早于聚合功能的自托管实例上。把它保持在一个合理的值,可以让这类小部件依然可用。
数据中的空档
没有任何记录匹配的桶会从结果中省略,而不是返回零。对平均值来说这是诚实的答案——空无一物没有平均值——但对于间歇性事件的计数图表,这意味着折线图会在无数据期间画出一条直线穿过,而不是掉到零。柱状图能自然地显示出空档;对于折线图,请注意区分”没有数据”与”零”。
实时更新
聚合小部件保持实时更新。记录到达时,受影响的桶会被重新计算并刷新小部件——最快每隔几秒一次,因此高频发布数据的设备不会淹没看板。由于最新的那个桶通常还在填充中,它的值会不断变化直到该桶关闭;这是预期行为,也正是为什么对于计数和总和,实时图表的最后一个点常常低于相邻的点。
性能与长历史数据
即便是聚合,跨越长时间窗口时仍然要读取其背后的记录。对于经常支撑长窗口图表的表,应用可以在其数据模式中声明持续降采样——参见数据后端参考中的 downsample。平台随后会维护该表的一份预聚合副本,并用它来回答长窗口查询,速度通常快上几个数量级;而且这份降采样历史可以比原始数据本身保留更久——因此即使原始记录已被删除,图表仍能展示两年的每小时平均值。
这对看板完全透明:小部件配置无需改动,而预聚合副本无法满足的查询(非常细的间隔、针对其未分组列的过滤条件)会自动回退到原始表。
限制
- 聚合适用于表,不适用于转换表。请改为聚合其底层表,或在转换自身的 SQL 中预先聚合。
- **每个数据系列只能用一个方法。**同一列需要多项统计时,请使用多个数据系列。
- **时间戳只能用计数聚合。**对时间戳求
平均值没有意义,会被拒绝。 - 对同一个数据系列而言,类别轴与时间轴互斥——一张图表要么是”随时间变化”,要么是”按类别”。若两者都想要(每台机器的每小时平均值),请把
x保持在tsp,并把机器列放入 pivot。 - JSON 字段可以像普通列一样聚合,但只有应用为降采样声明过的字段才能受益于预聚合副本;其余字段仍从原始表计算。
配置速查
| 需求 | x | y | 方法 | 间隔 |
|---|---|---|---|---|
| 一个月内的温度趋势 | tsp | temperature | 平均值 | 自动 |
| 每小时产量 | tsp | tsp | 计数 | 固定,1 小时 |
| 每天能耗 | tsp | kwh | 总和 | 固定,1 天 |
| 每台机器每小时的峰值功率 | tsp | power(外加 pivot machine) | 最大值 | 固定,1 小时 |
| 今天每台机器的记录数 | machine | tsp | 计数 | —(类别) |
| 本周每条产线的平均节拍时间 | line | cycle_time | 平均值 | —(类别) |
| 今日产量(KPI 卡片) | — | tsp | 计数 | —(单值) |
摘要
- 在过滤器对话框中把绑定从原始行切换为按时间聚合,让数据库计算数值,而不是传输记录。
- 把
x放在时间戳上,得到时间序列——看趋势用自动分辨率,间隔本身就是问题时用固定间隔。 - 把
x放在其他列上,得到每个类别一个值;使用标量绑定则得到一个实时数字。 - 对
tsp列使用计数,就是统计记录条数的方法。 - 过滤器在聚合之前生效;空桶会被省略;行限制退化为兜底保护。
- 对于长历史数据,应用可以声明
downsample,让平台从预聚合副本中提供图表数据,并把这段历史保留到超出原始数据的保留窗口之外。