设备数据最麻烦的地方,不是某一天突然写入一批数据,而是每秒都会有新数据进来。温度、压力、振动、电流、流量等指标不断产生,设备数量增加后,数据量几乎只会单向增长。业务页面通常只问两类问题:某台设备最近一小时的曲线,以及一批设备在某个时间段内的统计结果。数据库管理员面对的却是另一组问题:表拆到什么粒度,索引建在哪些表上,旧数据如何清理,新增设备是否需要发布脚本。
传统时序数据库方案经常把这些事情交给应用层。按月份、按设备或按两者组合拆表,确实能把数据分散开,但路由、建表、补索引和归档也随之进入业务代码。表数量还不多时,人工维护可以接受;当设备接入变成持续动作,分片规则就会变成一套需要长期维护的基础设施。
KingbaseES 超表提供了另一种组织方式:应用只面对一张逻辑表,物理层根据时间自动划分 Chunk,查询仍然使用标准 SQL。物理分块没有消失,它只是从业务代码里退回到数据库内部。对于需要持续写入和按时间查询的设备遥测、监控指标、日志采样等数据,这个变化比"再增加一个分片脚本"更实际。
表越拆越多,业务代码先变复杂
假设设备平台每天接收一批遥测数据,最初只有几十台设备,表名可能按月份拆成 device_metric_202607、device_metric_202608。查询当天数据时,应用知道应该访问哪张表,归档任务也只需要处理上个月的表。
问题从设备数量和采集频率一起增长后开始显现。按月拆表仍然太大,于是有人建议再按设备拆分;另一条链路为了减少单表索引,又采用"月份 + 设备类型"的组合。几种规则并存时,写入服务需要计算目标表,查询服务需要拼接表名,报表还要把多个物理表 UNION ALL 起来。新设备上线不只是插入一行设备信息,还可能意味着创建新表、复制索引、补权限和修改路由配置。
这种方案的隐性成本不只在 SQL 长度。一个迟到的数据可能属于上个月的表;历史数据回补时,写入程序必须知道旧分片的命名规则;删除保留期之外的数据时,运维人员既要确认时间范围,还要防止误删仍在使用的设备表。每一个规则都能单独工作,组合起来却很难让所有人都记住。
超表把应用侧的表名路由拿掉,业务表仍然保留设备、指标和采样时间这些真实字段。应用不需要知道 Chunk 的名字,也不需要为每个时间窗口生成一组 UNION ALL。

左侧的复杂度来自多套路由和维护动作:同一个写入入口要找到不同月份或设备的物理表,索引和归档任务也要跟着分片数量增长。右侧只有一个逻辑入口,系统按时间把写入和查询送到相应的物理 Chunk。这个结构并没有承诺所有查询都会自动变快,但它确实把"应用如何找到数据"的代码从业务层移开了。
先按业务字段设计一张逻辑表
时序数据表的第一步不是决定拆几张表,而是把每条采样记录描述完整。下面的字段适合做设备遥测示意:ts 是采样时间,device_id 标识设备,metric_code 标识指标,metric_value 保存数值,质量码和位置字段用于后续筛选或关联。
sql
CREATE TABLE device_metrics (
ts timestamptz NOT NULL,
device_id varchar(64) NOT NULL,
metric_code varchar(64) NOT NULL,
metric_value numeric(18, 6),
quality_code integer,
location varchar(128)
);
时间列应保持可比较的时间类型,不要在写入时先转换成日期字符串。设备编码和指标编码使用稳定的业务键,避免把设备名称当作分片键;设备改名不应该导致历史数据重新搬家。质量码是否参与查询、位置字段是否需要空间维度,要由实际访问模式决定。
在金仓时序组件支持的版本中,普通表可以通过 create_hypertable() 转换为超表。常见的时间维度写法如下,函数签名和组件安装状态需要在目标环境中先确认:
sql
SELECT create_hypertable('device_metrics', 'ts');
转换后,device_metrics 仍是应用使用的逻辑表名。数据库内部会根据时间边界创建和维护 Chunk,新数据写入时落入对应的时间块,时间范围查询则可以排除不相关的块。开发者不需要在 SQL 中拼接 device_metrics_202607 这样的物理表名。

图中三层关系比较清楚:最左侧是一张业务能看到的逻辑表,中间由数据库根据时间判断边界并路由,右侧才是实际保存数据的多个时间 Chunk。查询条件带有时间范围时,优化器有机会只访问相关块;没有时间条件的全表统计仍然可能读取大量数据,超表不会替应用补上缺失的过滤条件。
查询仍然是普通 SQL
设备平台最常见的报表是按小时聚合。只要时间范围写成左闭右开,跨天查询时不会重复计算边界记录,也便于后续改成按天或按班次统计。
sql
SELECT device_id,
date_trunc('hour', ts) AS hour_bucket,
avg(metric_value) AS avg_value,
max(metric_value) AS max_value,
min(metric_value) AS min_value,
count(*) AS sample_count
FROM device_metrics
WHERE ts >= TIMESTAMPTZ '2026-07-01 00:00:00+08'
AND ts < TIMESTAMPTZ '2026-07-02 00:00:00+08'
AND metric_code = 'temperature'
AND quality_code = 0
GROUP BY device_id, date_trunc('hour', ts)
ORDER BY device_id, hour_bucket;
这段查询没有引用任何 Chunk 名称。时间条件提供了数据裁剪的依据,指标和质量码则负责缩小块内扫描范围。若把 ts 包在 to_char(ts, ...) 或其他格式化函数里再比较,数据库很难利用时间列上的访问路径;时序查询应优先保留原始时间列的范围条件。
单设备最新值也可以用窗口函数完成,不需要查询服务先猜测设备对应的物理表:
sql
SELECT device_id,
metric_code,
ts,
metric_value,
quality_code
FROM (
SELECT device_id,
metric_code,
ts,
metric_value,
quality_code,
row_number() OVER (
PARTITION BY device_id, metric_code
ORDER BY ts DESC
) AS rn
FROM device_metrics
WHERE ts >= now() - interval '10 minutes'
) AS latest
WHERE rn = 1;
时序表也不是孤立的数据仓库。设备名称、产线和区域仍然可以放在关系表中,通过标准关联补齐展示字段:
sql
SELECT d.line_name,
m.device_id,
m.ts,
m.metric_value
FROM device_metrics AS m
JOIN device_registry AS d
ON d.device_id = m.device_id
WHERE m.metric_code = 'pressure'
AND m.ts >= TIMESTAMPTZ '2026-07-01 08:00:00+08'
AND m.ts < TIMESTAMPTZ '2026-07-01 09:00:00+08'
ORDER BY d.line_name, m.device_id, m.ts;
应用保留了关系型数据库熟悉的事务、约束和关联能力,时序数据只是在物理层采用了更适合时间范围访问的组织方式。这样,采集服务、告警服务和报表服务可以共用同一套表结构,不需要为每个服务维护一套分片路由。
写入端也应尽量按批次提交,并在入库前校验时间戳和设备编码。批次大小、事务持续时间和网络重试策略会直接影响写入端的锁与日志压力;超表接管的是物理落点,不会替采集服务修正错误时间或重复消息。
Chunk 透明,不等于不用治理
超表解决的是"如何把一张逻辑表拆成可管理的物理块",并不意味着所有容量和生命周期问题都自动消失。Chunk 时间间隔过小,可能产生大量小块;间隔过大,单块又会变得笨重。这个参数应结合写入速率、查询窗口、索引大小和磁盘规划验证,不能照搬别的项目。
迟到数据是另一个容易被忽略的场景。采集网关断网后,设备可能在几个小时后补发旧时间戳数据。数据库需要能够把这条记录写入对应的历史 Chunk,应用则要明确允许多长时间的回补窗口。超出窗口的批量回补,应单独评估锁、索引和日志空间。
索引也要围绕真实查询设计。常见的组合是时间范围加设备或指标过滤,但是否需要把 device_id 放在时间列之前,取决于查询是"单设备长时间曲线"还是"全设备某一时刻横向比较"。盲目给每个字段建索引,会把写入放大到每个索引维护动作上。先保留一组能覆盖主要查询的索引,再从执行计划和实际慢查询中补充,维护成本会更可控。
数据保留策略应与业务口径一起确定。在线曲线可能只看最近三个月,质量分析却需要保留一年;原始采样和小时聚合也不一定放在同一张表。可以把原始数据、聚合结果和归档数据分层治理,而不是把所有数据都留在热表里。删除历史数据前,要确认告警追溯、审计和补算任务不会再访问那段时间。
时间维度之外,还要考虑空间维度
当设备数量达到较大规模,仅按时间分块可能仍会让单个时间块包含过多设备。部分金仓时序能力支持时间维度与空间维度组合分区,例如把 device_id 作为分区依据。组合维度的具体函数参数、空间分区数量和可用版本以目标环境的时序组件文档为准,不能把某一套配置直接当成所有环境的通用命令。
组合分区也会带来新的选择:空间键的基数太低,分布不均;基数太高,Chunk 数量膨胀。设备注册表中的静态属性适合做关联条件,但不一定适合做空间分区键。决定分区键之前,先统计设备数量、每台设备的写入频率以及主要查询的设备过滤比例,数据分布比字段名称更能说明问题。
从一张表开始,逐项确认能力边界
落地前可以用系统目录确认目标实例是否安装了相关时序组件,再确认 create_hypertable 的函数签名:
sql
SELECT extname, extversion
FROM extension
ORDER BY extname;
SELECT n.nspname AS schema_name,
p.proname,
get_function_identity_arguments(p.oid) AS arguments
FROM proc AS p
JOIN namespace AS n
ON n.oid = p.pronamespace
WHERE p.proname = 'create_hypertable';
确认函数存在,只能说明接口可见,不能代替完整回归。还需要验证普通 INSERT、带时间范围的聚合、跨 Chunk 查询、迟到数据写入、事务回滚、备份恢复和权限控制。应用账号只授予业务需要的表权限,管理 Chunk、调整参数和清理历史数据的权限留给数据库运维账号,避免把自动化程序直接放进最高权限角色。
如果目标是把数据扩展到多节点,超表和分布式能力也要分开验收。超表负责逻辑统一和物理分块,分布式部署还涉及节点拓扑、分布键、故障切换、跨节点事务和备份策略。两者可以组合,但不能仅凭"已经转换为超表"就推导出水平扩展已经完成。
设备数量持续增长时,应用代码最希望保持稳定:写入仍然是对 device_metrics 的 INSERT,报表仍然是带时间条件的 SELECT,设备和产线仍然可以通过关系表关联。数据库内部负责 Chunk 的创建、路由和生命周期管理,运维工作从"每天维护一组表名"转向"观察分块是否均衡、查询是否带时间条件、保留策略是否符合业务"。
时序数据库的价值,最终要落到这种日常变化上。开发者面对的是一张表,数据库面对的是一组按时间组织的物理数据块;两边的职责边界清楚,海量写入带来的复杂度才不会全部堆在业务代码和人工脚本里。KingbaseES 超表并不替代数据建模、索引设计和容量治理,但它把最容易反复出错的分片路由交给数据库处理,让时序数据管理回到标准 SQL 和可持续运维的轨道上。