在这里插入图片描述

最近在跟几个搞工业物联网的朋友聊天。大家都在抱怨同一件事。就是现在都在搞AI,想用AI去判断一台设备是不是出了故障。但是AI根本看不懂业务。这是为什么呢?原因在于数据没给够。你只给AI看当前温度是50度,它怎么判断?50度可能是坏了,也可能是负载高了。AI得看过去一段时间的曲线。还得看振动数据,看电流数据。甚至还得去翻这台设备上个月的维修记录。这就需要连续的时序数据。

但是问题来了。数据在哪呢?通常来说,设备监控在一个系统里。维修工单在另一个系统里。设备台账又在一个系统里。数据全散了。过去各管各的还行。现在要做实时分析和智能判断,这数据就得来回倒腾。链路特别长。而且很容易出现更新不及时的情况。为了解决这个问题,金仓搞了一个KES TimeSeries的时序能力。它不是单独外挂的一个模块。它是直接建在KES融合数据库架构里面的。也就是说,时序数据、关系数据、GIS数据、向量数据,能在同一个库里直接关联。

不过今天咱们不聊那些大的AI场景。咱们就聊一个特别基础、特别底层的东西。也就是当你决定要把时序数据存到数据库里的时候,底层的存储架构到底该怎么搞。这也是很多做传统时序数据库的兄弟们最头疼的一件事。

一、传统时序数据库在分片管理上的那些麻烦事

只要你做时序数据,数据量往往都是非常大的。一台设备一秒钟采几个点。一万台设备呢?一天下来就是几十亿条数据。单台机器肯定存不下。那就得分散存。这就引出了分片的概念。

1.1 分片规则怎么定是个头疼事

在传统的时序数据库里,分片往往仅仅只是把数据按某个规则打散到不同的节点上。那规则怎么定呢?有的库是按设备ID来哈希。有的库是按时间范围来切。你选什么规则,直接决定了后面的查询快不快。

比如你按设备ID哈希。那查某一台设备的历史数据确实快。但是你要查某个时间段所有设备的平均值呢?这就得去所有节点扫一遍。非常慢。那如果按时间切分呢?查某个时间段是快了。但是查单台设备的历史,又得跨好几个节点去拼数据。这是一个很难两全的事情。一旦初期规则没定好,后面业务一变,你想改分片规则。那基本等于把数据全倒出来重新洗一遍。

1.2 后期扩容简直是一场灾难

业务发展太快了。刚开始可能只有一千台设备。你建了三个节点。过了半年,变一万台设备了。节点不够用了。你得加机器吧。

在传统的架构下,加机器哪有那么简单。你加了一个新节点,数据得往那边挪吧。这就叫数据重平衡。在重平衡的过程中,系统负载会飙升。而且很多传统时序库在做这个操作的时候,往往会影响正常的读写。业务方那边就会跑过来找你,说怎么系统这么卡。更麻烦的是,有些分片策略在扩容的时候,需要手动去指定哪些数据迁移到新节点。运维人员得写一堆脚本去算。算错一点,数据就丢了或者查不到了。

1.3 运维兄弟们往往仅仅只是成了填坑的工具人

因为分片是暴露给用户的。你在传统时序库里,往往能看到一个个具体的物理分片。你得去监控这些分片的大小。有的分片特别大,有的特别小。这叫数据倾斜。你得去处理。

某个节点宕机了,分片挂了。你得知道怎么把它切到备用节点上。这些底层细节全压在运维人员身上。对于企业层级里的应用来说,这其实是一个很大的隐患。因为招一个懂时序数据库底层分片原理的运维,比招一个懂普通关系库的人难多了。成本也高得多。

二、KES的超表架构是怎么个思路

面对上面说的这些麻烦,金仓在KES里引入了一个叫“超表”的东西。这个架构的思路跟传统时序库完全不一样。

2.1 超表到底是什么?其实就是一个逻辑壳子

咱们不要被这个名字吓到。超表,英文叫Hypertable。它到底是什么呢?其实你在KES里建完一张超表之后,你在数据库里看到的就是一张普普通通的表。它就是一个逻辑层面的壳子。

对于写代码的兄弟来说,你面对的就是这一张表。你往这张表里插数据,从这张表里查数据。你根本不需要知道底下到底有几台机器。也不需要知道数据被切成了多少块。这种对应用层完全透明的设计,是超表架构最核心的地方。

2.2 物理Chunk对应用层完全透明

那么数据到底存在哪了呢?在超表的下面,KES偷偷地建了很多小的物理表。这些物理表有个专门的名字,叫Chunk,也就是数据块。

当你往超表里插一条数据的时候。KES的底层会根据你建表时设定的规则,算出这条数据应该落在哪个Chunk里。然后直接把数据塞到那个物理表里。你写的是一条 INSERT INTO sensor_data ... 的SQL。KES底层可能把它转成了 INSERT INTO _chunk_sensor_data_1 ...。但是这个转换过程,你不需要管。

查数据也是一样的道理。你敲一句 SELECT * FROM sensor_data WHERE time > '2026-01-01'。KES接到这句SQL。它不会去扫整张超表。它会去查元数据。看看符合这个时间条件的Chunk到底有哪些。接着它就把查询下推到那几个具体的Chunk上去执行。最后把结果拼起来返回给你。你感觉不到这中间发生了什么。

为了让大家看清楚这个关系,我画了个简单的架构图。

标准SQL写入/查询

根据时间/空间规则

根据时间/空间规则

根据时间/空间规则

应用层开发者

超表 Hypertable 逻辑表

KES 底层路由调度

物理数据块 Chunk 1

物理数据块 Chunk 2

物理数据块 Chunk N

节点1 存储

节点2 存储

节点N 存储

你看这个图。上面就是一张逻辑表。下面是对应的一堆物理块。开发者只跟上面打交道。下面的乱七八糟的分配,全是KES自己在搞。

2.3 为什么这种透明化很重要

你可能会问,这不就是分库分表中间件干的事吗?还真不一样。中间件是在外面套了一层。SQL解析和路由都在应用和数据库之间。这多了一跳,性能就有损耗。而且中间件很难做到深度的查询下推。

KES的超表是数据库引擎内部原生实现的。路由逻辑就在存储引擎里。没有网络开销。而且它对事务的支持更好。你在一个超表里做跨Chunk的查询,甚至更新,它能够保证操作的正确性。这是外面加个中间件很难做到的。

三、系统根据时间自动创建和管理数据块的能力

超表好是好,那底下的Chunk怎么管理呢?如果还要人去建Chunk,那不是换了个名字折腾人吗?这就得说到KES的自动分区能力了。

3.1 单纯按时间切分的玩法

时序数据最明显的特征就是带时间戳。所以按时间切分是最自然的。你在KES里创建超表的时候,可以指定一个时间间隔。比如你指定7天。

那么系统会怎么干呢?当你插入第一条数据的时候,系统发现当前时间对应的数据块还不存在。它就会自动在底层创建一个Chunk。这个Chunk管接下来7天的数据。等过了7天,有新的时间戳的数据进来了。系统一看,超出当前Chunk的范围了。它又会自动建一个新的Chunk。

这个过程完全不需要人工干预。你不用去写定时任务去建表。也不用担心哪天忘了建表导致数据写不进去。系统自己就把这事办了。

3.2 时间加上空间组合的切分玩法

如果是企业层级里的复杂工业场景,往往仅仅只是按时间切是不够的。比如北京轨道交通那种场景。那么多条线路,那么多设备。如果只按时间切,那一个时间块里的数据量还是大得惊人。

KES的超表支持时间加上空间的组合分区。什么是空间?在这里可以理解成设备ID,或者是设备的地理位置、所属产线这些维度。

你可以设定规则。比如按7天时间,加上设备所属的线路来切分。这样的话,底层生成的Chunk,不仅时间上是隔离的,在设备维度上也是隔离的。这种情况下,你要查某条线路某一天的设备数据。KES底层就能精准定位到唯一的一个Chunk。查询速度会非常快。

3.3 完全不需要人工干预的自动管理

不仅建Chunk是自动的。对于旧Chunk的处理,KES也能帮你干。

时序数据有个特点。越老的数据,被查的频率越低。但是为了合规或者做模型训练,你又不能删。在传统的做法里,DBA得定期写脚本,把大表里的老数据导出来,转成列式存储,或者挪到便宜的硬盘上。

在KES里,这事儿简单了。因为是按时间自动分块的。那些几个月前的Chunk,物理上就是独立的一张张表。KES可以直接对这些旧的Chunk进行底层的存储转换。比如把原本按行存的Chunk,转成按列存。因为老数据基本就是用来做全表扫描分析的,列存更合适。而且转的过程是在后台异步做的。你的业务查询完全不受影响。

甚至于,如果你想把这个旧Chunk挪到廉价的存储节点上。KES也能通过底层指令把这张物理表迁移走。而你的业务SQL,还是查那张超表。完全感知不到数据已经不在原来的硬盘上了。这就把运维的工作量打下来了。

四、既保留了关系库的易用性,又有了扩展能力

聊到这里,你可能会觉得,这听起来像是个NoSQL数据库。其实真不是。这也是KES超表架构最讨巧的地方。

4.1 开发人员还是写标准的SQL

用过InfluxDB或者TDengine的兄弟都知道。它们往往有自己的特殊SQL语法,或者干脆就用它们自己的行协议写入。你学了一套MySQL或者Oracle的SQL。到了时序库里,发现很多语法不通用。你又得学一套新的。比如特殊的函数名,特殊的保留字。

KES的超表不一样。因为它底层就是KES,一个标准的关系型数据库。你对超表的操作,完完全全就是标准SQL。

你要插入数据。你就写普通的 INSERT INTO 语句。你要查数据聚合。你就写标准的 SELECT ... GROUP BY 语句。你要跟业务库里的设备台账表做关联。你直接写 JOIN 就行了。

比如下面这句。

SELECT t.device_id, d.device_name, AVG(t.temperature) 
FROM sensor_data t 
JOIN device_info d ON t.device_id = d.id 
WHERE t.time > '2026-07-01' 
GROUP BY t.device_id, d.device_name;

这完全就是普通关系库的写法。不需要去查时序库特有的文档。这对开发兄弟来说,学习成本几乎为零。原有的代码稍微改改表名和字段,就能跑起来。

4.2 水平扩展能力其实藏在底层

虽然你用的是标准SQL,面对的是单张逻辑表。但是KES在底层是支持分布式架构的。

前面说的那些Chunk,是可以分布在不同的物理节点上的。当你发现单机的CPU或者磁盘撑不住的时候。你可以在KES集群里加新的数据节点。KES的调度器会自动把未来新产生的Chunk,分配到新节点上去。

对于应用来说。你的连接串没变。你的SQL没变。但是你的系统总吞吐量却上去了。这就是所谓的水平扩展能力。它把分布式数据库的扩展性,包装成了单机关系库的使用体验。

4.3 写入和压缩的一些实际表现

文章开头提到的资料里,讲到了KES时序库的一些具体性能数据。这里也可以顺带提一下。因为超表的底层是独立的Chunk。写入的时候,其实往往只是往当前最新的那一个Chunk里追加数据。这非常适合时序数据只写不改的特征。

KES在写入端做了很多优化。比如Append追加写、无锁化、异步IO。这就减少了高并发写入时候的资源等待。资料里说在特定测试环境下,单节点写入能达到千万级指标点每秒。这个数字还是很吓人的。

存下来之后呢?Chunk里的数据是按列存方式组织的。KES用了Delta-of-Delta增量编码,还有Gorilla浮点数压缩这些时序专用的算法。它会根据不同数据类型自动去匹配压缩方式。数字型的时序数据,压缩比能到10比1。也就是说,存100G的数据,实际只占10G的硬盘。存储空间能省下90%。这直接把硬件成本打下来了。

五、多模融合才是这套架构的最终目的

如果超表仅仅只是解决了时序数据的存储和查询。那它也就是个不错的时序库而已。但是放在KES整个架构里看,它的意义远不止于此。

5.1 把散成渣的数据连起来

回到文章开头说的AI落地的问题。AI要判断设备异常。光有时序曲线不够。你得知道这台设备是哪个厂家造的。它装在哪个车站。它上个月修过什么零件。

如果是传统的方案。你的时序库里有曲线。你的关系库里有台账。你的GIS库里有位置。你的向量库里可能有维修手册的嵌入向量。你要做一次AI推理,得把这些库的数据全抽出来,在应用层拼装好,再喂给AI。

现在有了KES的超表和融合架构。时序数据存在超表的Chunk里。关系数据就存在普通的业务表里。它们在同一个数据库实例里。你直接一句SQL,通过设备ID这个主键,就能把时序数据和关系数据 JOIN 出来。如果你要用向量检索找相似的故障案例,也能在同一个库里面调用向量相关的函数。

这种多模态数据的融合,使得数据获取的链路变得极短。不需要经过多次提取和转换。数据也不会出现更新不及时的情况。AI拿到的上下文,永远是完整的、实时的。

5.2 业务落地到底是个啥效果

咱们不扯虚的,看实际案例。资料里提到了北京轨道交通应急指挥调度平台。他们用了金仓的时序数据库之后。

写入性能比原来的系统提升了超过10倍。原来可能要好几秒才能写进去的数据,现在瞬间就进去了。部分历史分析的查询,从分钟级缩短到了秒级。你拉一个趋势图,以前要等个一两分钟转圈,现在点一下就出来了。还有最实在的,时序数据占用的存储空间,降低了70%到80%。这意味着原来可能要买十个磁盘阵列,现在两三个就搞定了。

这些能力首先支撑了实时的监控和故障追溯。当业务进一步引入预测模型的时候,因为底下的数据架构已经把多模态数据打通了。AI模型直接就能在这个库上面跑。不需要再去搞复杂的数据搬运。

六、最后随便聊两句

其实做技术的都知道,没有一种架构是完美的。KES的超表架构也一样。它在底层把分片和分布式细节屏蔽了,这必然会在元数据管理上带来一些开销。如果是极其极端的并发场景,路由层的压力也是存在的。

但是对于绝大多数企业层级里的工业物联网、能源电力这些场景来说。这种架构带来的收益,远远大于它那点开销。你不需要专门招一个懂分片运维的人。你的开发人员不用去学一套奇怪的语法。你的数据还能和业务数据无缝结合。

对于很多正在做国产化替换,或者正在为时序数据选型的兄弟来说。与其去折腾那些分片规则、扩容脚本,不如找个时间去搭个KES的环境。把超表建出来,往里面灌几千万条数据试试。用标准的SQL去查一查,JOIN一下业务表。感受一下这种把复杂度交给数据库,把简单留给开发者的感觉。实践出真知,试过了,你才知道这东西到底好不好用。

更多推荐