尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

时序数据库的“超表“到底是个啥?我花两周拆开了金仓这套架构

时序数据库的“超表“到底是个啥?我花两周拆开了金仓这套架构 文章目录先搞明白一个问题为什么传统分区表搞不定时序数据超表的第一层把物理分片藏起来超表的第二层二维分区——不只是按时间切查询这边分区裁剪和标准SQL的化学反应上篇小结兼容是对前人努力的尊重是确保业务平稳过渡的基石然而这仅仅是故事的起点说个真实经历。去年年底接了个工业物联网的项目客户是做设备监控的前端接了大概三千多个传感器点位温度、振动、电流什么的都有。刚上线那会儿一切正常跑了三个月之后问题就来了——写入开始变慢查询更慢运维那边天天跟我抱怨说数据量上来之后整个系统跟老牛拉破车似的。我当时第一反应是加索引、调参数折腾了一轮发现没啥用。后来仔细一查好家伙底层分区表已经膨胀到上千个分片了光查询规划器遍历元数据就得吃掉大几十毫秒。再加上分片策略是初期拍脑袋定的有些分片数据量差了好几倍典型的热点倾斜。最后那个项目花了大力气重新做数据迁移才算把坑填上。但这段经历让我对时序数据库的分区管理这件事有了很深的警惕。今年有机会深入体验了金仓数据库的时序场景性能增强包里面有个核心概念叫超表Hypertable说白了就是针对我上面说的这些痛点设计的一套东西。这篇文章算是我的体验笔记把超表的架构逻辑拆开来讲讲。先搞明白一个问题为什么传统分区表搞不定时序数据用金仓那份官方文档里的话说时序场景的核心需求就四条海量存储设备多、指标多、频率高、持续写入数据不停产生、特定时间段的聚合查询优化、以及数据生命周期管理过期的数据要能自动处理。传统分区表在这几个方面都有短板。拿分片管理来说你得自己提前规划好分区策略——分多少片、每片多大、什么时候创建新分区。这些全得手动搞写脚本、配定时任务。一旦脚本出了问题比如某个时间段的分区没创建出来数据写入就直接报错。更麻烦的是扩容。设备增加了、采样频率提高了当初设定的分区策略可能就不合适了。但改分区策略这件事在傳統方案里基本等于做一次小型数据迁移。还有一点容易被忽略的——元数据膨胀。每个分区都有自己的索引、统计信息和路由信息。当分区数从几十个涨到几百个、上千个之后数据库在做查询规划的时候光遍历这些元数据就得花不少时间。我前面说的那个项目就是这个情况查个最近一小时的数据还没开始真正扫描数据呢光看元数据就慢了一拍。超表的第一层把物理分片藏起来金仓时序数据库的超表最核心的设计思路就一句话——让开发者只面对一张逻辑表底下的物理分片全交给系统自己管。这话说起来简单做起来涉及的东西还挺多的。我从使用手册里梳理了一下超表的分区机制大概是这么运作的每个超表底下由很多叫Chunk的子表组成。每个Chunk对应一个时间范围只包含这个时间范围内的数据。关键的是当插入的数据时间戳不在已有Chunk的时间范围内时系统会自动创建一个新的Chunk来存储这些数据。你不需要写脚本不需要配定时任务系统自己就干了。默认情况下每个Chunk覆盖的时间周期是7天。但这个值可以通过chunk_time_interval参数来调整。使用手册里给了个很实际的建议如果每日写入量大概2GB、内存64GB那7天的间隔就合适如果每日写入量到了10GB那间隔就应该设成1天。这个设计的好处在哪呢拿我前面那个项目来说如果当时用的是超表分区数量膨胀的问题就不会出现。因为Chunk的创建是自动的而且每个Chunk的时间范围是受控的不会无限制膨胀。来看个具体的创建过程-- 第一步先建一张普通表跟平时建表一模一样CREATETABLEsensor_data(timeTIMESTAMPTZNOTNULL,device_idTEXTNOTNULL,temperatureDOUBLEPRECISION,humidityDOUBLEPRECISION,vibrationDOUBLEPRECISION);-- 第二步一行SQL把它变成超表SELECTcreate_hypertable(sensor_data,time);就这两步。第一步行是标准的CREATE TABLE没有任何特殊语法。第二步调用create_hypertable函数告诉系统这张表按time列做时间分区。完事了。如果你想调整分区间隔创建的时候直接指定就行-- 创建超表时设置分区间隔为1天SELECTcreate_hypertable(sensor_data,time,chunk_time_intervalINTERVAL1 day);已经存在的超表也能改间隔不过要注意——改了之后只对新建的Chunk生效老的Chunk不受影响。所以初期选个合理的值还是挺重要的-- 调整已有超表的块间隔SELECTset_chunk_time_interval(sensor_data,INTERVAL24 hours);从应用层的角度看超表就是一张普通表。INSERT的时候不需要指定数据写到哪个Chunk里系统根据时间戳自动路由。SELECT的时候也不需要关心数据分布在哪些Chunk上该JOIN就JOIN该GROUP BY就GROUP BY。这一点特别重要。因为很多时序库搞了一套自己的专有API或者查询语言开发团队得重新学原来的代码也不能复用。超表的第二层二维分区——不只是按时间切金仓的做法是再加一个空间轴。用设备ID或者区域编号做哈希分区把同一时间段内的数据均匀分散到不同的物理节点上。这样一来写入压力就被分摊了整体吞吐量跟节点数成正比可以线性扩展。从使用手册里看加空间维度是用add_dimension函数-- 先创建按时间分区的超表SELECTcreate_hypertable(device_metrics,time);-- 再加上空间维度按device_id做4路哈希分区SELECTadd_dimension(device_metrics,device_id,number_partitions4);这个设计在金仓官方的那篇工业时序数据的文章里也有详细说明。原文是这么说的金仓时序数据库自研创新二维分区算法通过分布式超表和块级数据管理机制结合时间和空间两个维度的内核级精密路由。时间轴主分区用于控制数据块和索引规模避免热数据范围无限扩大空间轴次分区针对设备ID等标签执行哈希分区将高并发写入压力均匀分散至各数据节点。我用测试环境做了个简单的对比。在只按时间分区的情况下模拟10000个设备并发写入写入延迟波动比较大大概50到200毫秒之间跳动。加上4路空间哈希之后写入延迟基本稳定在10到30毫秒。差距还是很明显的。而且这个差距会随着设备数量增加而放大。设备越多空间哈希的分散效果越好。查询这边分区裁剪和标准SQL的化学反应超表的查询体验我觉得可以用无感两个字来形容。你写SQL的时候完全不需要考虑底下有多少个Chunk、数据分布在哪些节点上。带时间范围的查询系统自动做分区裁剪只扫相关的Chunk。-- 查最近1小时的设备平均温度-- 系统自动跳过不相关的分区你什么都不用管SELECTdevice_id,AVG(temperature)ASavg_tempFROMdevice_metricsWHEREtimeNOW()-INTERVAL1 hourGROUPBYdevice_idORDERBYavg_tempDESC;想看执行计划是不是真的做了分区裁剪用EXPLAIN就行EXPLAIN(ANALYZE,BUFFERS)SELECTdevice_id,AVG(temperature)FROMdevice_metricsWHEREtime2026-08-20 00:00:00ANDtime2026-08-20 01:00:00GROUPBYdevice_id;执行计划里如果只出现了20260820相关的那个子分区就说明裁剪生效了。如果你发现所有分片都在被扫描那八成是查询条件里没有直接带上时间列或者用了函数包裹时间列导致优化器推不出来范围。时间桶聚合是时序场景里的刚需操作。金仓内置了time_bucket函数可以把时间戳按任意间隔截断配合GROUP BY做降采样-- 按分钟降采样算每分钟的平均温度SELECTtime_bucket(1 minute,time)ASminute_bucket,device_id,AVG(temperature)ASavg_temp,MAX(vibration)ASmax_vibFROMdevice_metricsWHEREtime2026-08-20 00:00:00ANDtime2026-08-21 00:00:00GROUPBYminute_bucket,device_idORDERBYminute_bucket,device_id;如果你的业务需要特殊对齐方式可以通过origin参数调整。-- 计算5小时间隔的平均值并把所有桶推迟1小时SELECTtime_bucket(5 hours,time,1 hour::INTERVAL)ASbucket,AVG(cpu)FROMmetricsGROUPBYbucketORDERBYbucketDESC;时序表和关系表的JOIN也很自然。超表在应用层就是一张普通表所以它可以跟任何其他表做JOIN不需要额外的数据同步或者中间层。-- 时序数据直接JOIN设备档案表SELECTd.device_name,d.location,d.maintainer,m.time,m.temperature,m.pressureFROMdevice_metrics mJOINdevice_archive dONm.device_idd.device_idWHEREm.time2026-08-20 00:00:00ANDm.time2026-08-20 01:00:00ANDm.temperatured.alert_thresholdORDERBYm.temperatureDESC;查询的时候系统会把已经预计算好的历史结果和最新的未聚合数据组合起来返回你得到的始终是最新的分析结果但不需要每次都去扫描海量原始数据。-- 创建一个按小时聚合的连续聚合视图CREATEMATERIALIZEDVIEWhourly_temp_statsWITH(timescaledb.continuous)ASSELECTtime_bucket(1 hour,time)AShour_bucket,device_id,AVG(temperature)ASavg_temp,MAX(temperature)ASmax_temp,MIN(temperature)ASmin_tempFROMdevice_metricsGROUPBYhour_bucket,device_id;连续聚合还支持分层。比如你可以先建一个按分钟聚合的视图再在上面建一个按小时聚合的视图层层递进。这样不同粒度的分析需求都有预计算的结果可以用。使用手册里说在典型的分钟级滑动窗口分析中连续聚合可以实现毫秒级响应。这对状态监测和故障识别这类需要持续获得最新分析结果的应用来说非常有用。自动刷新策略也可以配-- 添加自动刷新策略每1小时刷新一次覆盖最近2小时的数据SELECTadd_continuous_aggregate_policy(hourly_temp_stats,start_offsetINTERVAL2 hours,end_offsetINTERVAL1 hour,schedule_intervalINTERVAL1 hour);上篇小结写到这上篇差不多了。总结一下我对超表架构的理解超表本质上做了一件事——把分片管理的复杂度从开发者和运维身上移到了数据库内核里。传统的分区方案分片的创建、路由、裁剪、归档全得人手动搞。超表把这些全自动化了。开发者看到的是一张表写的是标准SQL运维看到是一组自动化策略底下的物理Chunk怎么创建、怎么路由、怎么裁剪那是内核的事。而二维分区的设计又在这个基础上解决了高并发写入的分散问题。时间轴控制数据块规模空间轴分摊写入压力两个维度配合起来才能撑住真正的大规模场景。连续聚合则进一步解决了查得快的问题——常用的分析结果提前算好查询的时候直接用不用每次都扫原始数据。下篇我打算聊聊压缩、数据保留策略、分布式超表还有几个实际落地项目的情况。北京轨交TCC、泰兴交通、机场预测性维护这些案例里都有不少干货。先写到这。
返回列表