关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

2026 服务器租用跑时序库怎么算磁盘 IO?TDengine 超级表与时间线数量实测对比 + 选型手册

发布时间:2026-09-29

2026 服务器租用跑时序库怎么算磁盘 IO?TDengine 超级表与时间线数量实测对比 + 选型手册

开篇:别问能存多少行,先问有多少条时间线

干了十几年数据库运维,我见过太多人跑来问"我要存三年数据,一天五千万条,得买多大的盘"。这个问题问错了方向。时序数据库不是这么算的——TDengine 的性能曲线不由数据量决定,由时间线数量决定。同样十个亿的数据点,分布在 1000 个采集点上和分布在 100 万个采集点上,是两台完全不一样的机器,写出来的 SQL 也完全是两种命运。

先把话挑明:所谓一条时间线(time series),就是一个持续产生数据的采集点——一台设备、一个温感探头、一辆车上的一个 CAN 通道、一个 Kubernetes Pod 的一路指标。在 TDengine 里,它对应一张子表。超级表(STABLE)是模板,子表才是真正落数据的地方。这个设计看着简单,但它决定了后面所有的 IO 行为:随机写还是顺序写、压缩能压到多少、查询要打开多少个文件句柄、内存会不会先于磁盘爆掉。

这篇不讲时序数据库的发展史,也不做产品横评。就一件事:把"时间线数量 → 资源消耗 → 该租什么机器"这条链路讲透。读完你应该能自己回答:我这个项目,是该买块大 NVMe,还是该加内存,还是该干脆把采集方案改了。

要点一,容量指标是时间线数,不是行数。行数堆到几百亿只是占空间,时间线涨到几百万才是真麻烦。

要点二,超级表 + 标签(TAGS)的模型,本质是拿一个 schema 换"相同数据在物理上挨着"这件事。随机 IO 变顺序 IO,这才是省钱的底层原理。

要点三,每多一条时间线,都要多一份元数据与索引条目。这份东西常驻内存,不会因为你数据删了就自动瘦身。

要点四,采集频率和时间线数量是两个独立变量。前者吃写入吞吐和 WAL 顺序写,后者吃内存和落盘块碎片。混为一谈,选型必然走偏。

要点五,磁盘别只看容量。WAL 对 fsync 延迟敏感,数据文件的 compaction 与查询是随机读,这两件事对盘的要求不一样,分开看才清楚。

超级表加子表:一个采集点一张表,这是省 IO 的关键

先用大白话把这个模型讲清楚,不然后面全是听不懂的名词。

超级表是模板,子表是实例

你在 TDengine 里建一张超级表,定义的是"这类设备长什么样":有一个时间戳主键,有若干个采集值列(比如温度、湿度、电流),还有若干个标签列(比如设备型号、所在车间、所属产线)。然后每接入一个真实设备,就 CREATE TABLE ... USING 超级表 TAGS (...) 建一张子表,标签值填这台设备的属性。

重点来了:这张子表就是一条时间线,一个设备一辈子的数据都往这里写。不是所有设备往一张大表里插一行 device_id,而是每台设备自己一张表。

换个角度看:为什么"一条时间线一张表"反而更省

很多从 MySQL、PostgreSQL 转过来的人第一反应是"几百万张表?疯了吧"。其实恰好相反。传统做法是一张大宽表,(device_id, ts)建联合索引,每次写入要在索引树中间找个位置插进去——B+ 树越大,这个"找位置"的动作越接近随机 IO。磁盘最怕的就是这个。

换成一条时间线一张表之后,写入逻辑退化成一件极其朴素的事:找到这张子表当前最后一个数据块,往后追加。时间戳天然递增,不用在中间插队,不用维护庞大的二级索引。说白了,这是用"表数量"换"写入的有序性"。表多了,但每张表内部永远是追加写。

这也是为什么 TDengine 在官方设计里不断强调"一个数据采集点一张表"。它不是为了方便,它是一个纯粹的 IO 优化决策。

子表自动创建这事儿,别偷懒手搓

实际接入的时候,不用挨个 CREATE TABLE。用 CREATE TABLE ... USING STABLE TAGS(...) 配合写入协议里的自动建表(schema-less 写入),设备第一次上报数据时子表就自动生成了。这点很关键,因为物联网场景动辄新增设备,靠人手工建表迟早出事。

但也不能完全放飞。见过一个项目,把设备固件版本号塞进标签做级联,固件一升级就生成一张新子表。一条物理设备被拆成几十条时间线,时间线数量凭空涨了几十倍,后患无穷。这个坑后面"五个坑"里还会细说。

TAGS 和 COLUMN 分开存,省掉的不只是空间

超级表的列分成两类,这个区分是 TDengine 数据模型的地基。

COLUMN:随时间变的采集值

温度、电压、CPU 使用率、订单金额——这些东西每一条记录都有一个新值,它们是真正的时序数据,按列式压缩存在数据块里。

TAGS:不随时间变的静态属性

设备型号、地理位置、所属客户、出厂批次——这些东西对一个采集点来说是固定的,或者变化极慢。它们在超级表的元数据区只存一份,并且建索引,而不是每一行采集数据里重复抄一遍。

省下来的不止是空间,还有 IO 路径的长度

空间层面好理解:假设一个标签字符串 20 字节,一千万条数据,不让它在每行里重复,就是几百 MB 到上 GB 的差距。但这只是小头。

真正值钱的是查询路径的变化。想查"一车间所有 A 型设备昨天的平均温度",引擎的执行顺序是:先在标签索引里过滤,命中一批子表;然后只扫这批子表的数据块。标签过滤发生在元数据层,几乎不碰数据文件。如果标签是普通的列,那就要全表扫描几十亿行去比对 device_type = 'A',这是完全不同的 IO 量级。

还有一层容易被忽略:列式存储 + 单一类型连续值,压缩率才能拉满。一个数据块里存的是同一种类型(都是 float、都是 int),连续值之间相关性强,delta 编码、游程编码、字典编码才有发挥空间。如果标签混在里面,一个块里又是字符串又是浮点数,压缩基本无从下手。

所以判断一列该放 TAGS 还是 COLUMN,标准就一句话:它会不会跟着时间戳一天变几万次?会,就放 COLUMN;不会,就放 TAGS。把静态属性误放进 COLUMN,既浪费空间又拖累压缩,是最常见的建模错误。

为什么同一条时间线的数据物理上挨在一起

这一节是整篇文章的枢纽,理解它,"为什么时间线数量决定一切"就顺理成章了。

时间提分区,然后再按 vnode 分片

TDengine 的数据组织可以粗略理解成两级:先按时间切成段(不同版本叫法不同,days / duration 之类,指的是单个数据文件覆盖的时间范围),再按 vnode 做水平切分。在时间维度上,一个时间段只对应一个数据文件组;在空间维度上,同一时刻的不同子表按哈希散落到不同 vnode。

这就带来一个很有意思的结果:同一个时间点上,A 表和 B 表的数据可能不在同一个 vnode 里,但 A 表自己的数据,在同一个时间段内一定落在同一处。

关键推论:单时间线查询 = 少量数据块

因为一张子表在一个时间段内的数据写在同一处,那么"查这台设备过去 24 小时的数据"这个请求,引擎定位到少数几个数据块,然后连续读出来就行了。磁头的移动距离、SSD 的读放大,都被压到最低。

换成传统的(device_id, ts)大宽表,同一个设备在索引里是逻辑上"挨着"的,物理页面却可能被相邻设备的写入挤得七零八落。逻辑有序不等于物理有序,这就是它慢的根源。

但注意,这个红利只对"单条时间线"成立

如果你的查询是"查同一时刻所有设备的值"(比如看某一秒全网设备状态),那在 TDengine 里就是跨 vnode、跨子表的多点随机读,捞不到这个便宜。相应地,如果你把查询反过来写成"查某台设备一段时间",它跑得飞快。

这就是为什么建模阶段一定要先想清楚查什么。超级表模型重度优化了"按设备查时间"这个方向,反过来那个方向不是不行,但你得知道自己在付出什么。

一个延伸:游览 Cartesian 积式的时间线设计

还有个反直觉的点:不要把多个测量维度塞进一张表。一个传感器同时测温度、湿度、振动三个值,正确做法是三列,不是三席位companying// 三张表的三倍时间线。把三个测量值做成一张表的三个 COLUMN,时间线数量是 1;拆成三张子表去写,时间线数量是 3,元数据、索引、文件句柄全部翻三倍。这个决策在小规模时看不出差别,到百万设备量级就是灾难。

时间线数量涨上去,先爆的是内存不是磁盘

现在可以回答那个被反复追问的问题了:为什么时间线一多,机器就撑不住。

元数据常驻内存,这是硬性成本

每张子表都要在内存里维护一份元数据:表名、schema 引用、标签值、索引条目、当前写入位置、最近数据块的偏移量等等。这份开销是常驻的,跟你这台设备今年上传了多少条数据关系不大。哪怕这台设备只在上线那天上报过一条数据然后就离线了,它那份元数据照样占着。

所以真正的估算公式是这个:内存需求 ≈ 时间线数量 × 单时间线元数据量 + 写入缓冲区 + 查询工作区。第一项在海量时间线场景里是绝对主项。

单条时间线的元数据具体占多少字节,取决于版本、标签数量、schema 复杂度,我给不了统一数字,这个必须以实测为准——不同版本的官方 metrics 都对得上,但落到你的 schema 上只有压测才知道。方法论是对的:先数清楚时间线,再用自己的 schema 压一遍,看每万条时间线的内存增量是多少,然后线性外推。

官方给的量级,和它的潜台词

TDengine 官方在不同版本里给过单库支持的时间线量级,百万级甚至更高是常见口径。但"支持"不等于"随便配台机器就能跑"。百万时间线在官方文档能跑,前提是你给它配了相应的内存。拿一台 16G 内存的机器去扛百万级时间线,崩是应该的,不崩才奇怪。

时间线过多的四个典型症状

这些是我在真实项目里反复见到的,比任何 benchmark 都好使:

内存持续上涨且不回落。这不是泄漏,是元数据在扩张。重启能降下来,跑几天又涨回去,基本可以确认。

落盘产生大量小数据块。高频优先处理的逻辑是:一个数据块攒够了才落盘。时间线一多,同一时刻能分给每条时间线的缓冲就少了,还没攒够就触发落盘,于是写下去的都是零碎小块。小块多,数据文件数量就爆炸。

查询要打开很多文件句柄。一次涉及几十万张子表的聚合查询,背后是大量的文件描述符。操作系统默认的文件句柄上限会被撞穿,报的是"too many open files",但根因在数据模型。

compaction 压力上升。后台要不断合并那些碎片小块,CPU 和磁盘 IO 都被后台任务吃掉,前台查询延迟跟着抖。

一个冷酷但有用的结论

如果你的时间线数量超出规划,加硬盘是没用的,加内存才是解药。见过有人数据文件涨了就去扩盘,扩完发现写入照样慢——因为他缺的不是空间,是元数据装不下。

vnode 是什么:一个 vnode 一套 WAL、一套数据文件

讲写入路径之前,得先把 vnode 这个概念钉死,因为它是 TDengine 资源隔离的最小单位。

vnode = 一个独立的工作单元

一个 vnode 拥有自己的 WAL、自己的内存表、自己的数据文件。它不是一个进程,也不是一个容器,而是一个逻辑上的"自治数据单元",由后台的写入/查询执行单元调度处理。

这个"每 vnode 一套"的结构意味着:vnode 数量决定了并行度。只有一个 vnode,写多 tenants 也只能串行在一个 WAL 上排隊;开到多个 vnode,写入和查询可以并行落盘。

vnode 太少:并行度不够

典型症状是 CPU 和磁盘都没跑满,但写入延迟已经很高。多线程打进去,全堵在同一个 WAL 追加上。这种情况加 CPU 核数是白扔钱,得调 vnode 数量。

vnode 太多:每个 vnode 都营养不良

反过来,vnode 开多了,每个 vnode 摊到的时间线变少,每个 vnode 的写入都不够密集,内存缓冲攒不满批,落盘次数反而变多,落地的又是一堆碎块。同时后台要维护的 WAL 和数据文件套数增加,管理开销和管理复杂度都上去了。

大致的取值逻辑

官方文档给过推荐量级,常见口径是按 CPU 核数走,具体数字以你所用版本的官方文档为准。这里给一条经验性的思路而不是公式:在数据量规划清楚之前就定死 vnode 数,基本等于猜。vnode 数量在建库时就基本定下了,后期调整代价不小,所以初始要用"未来一到两年的预计时间线总数 + 写入压力"来定,而不是用当下的量。

vnode 与磁盘怎么放

多 vnode 的另一个实际好处是可以把不同 vnode 的数据目录落到不同物理盘上(取决于版本是否支持多数据目录配置),把 IO 压力分摊开。在一台装了多块 NVMe 的裸金属上,这个能力很有用。反过来,如果只有一块盘,多 vnode 分摊的就是操作系统层面的队列,收益打折。

写入路径拆解:WAL、内存缓冲、列式落盘

这一节讲一条数据从网络进来,到躺到磁盘上,中间经历了什么。每一段都对应一种 IO 类型,而 IO 类型决定你该买什么盘。

第一步:写 WAL,先保命

数据进来先追加写 WAL(Write Ahead Log)。这一步的目的是持久性:进程突然挂了,内存里没落盘的数据能从 WAL 补回来(这个补回来的机制 MEM 与 WAL 配合完成,具体以官方文档为准)。

WAL 的 IO 特征非常鲜明:纯顺序写,但每一次 commit 都要 fsync。顺序写意味着机械盘也能扛住带宽,但 fsync 延迟是另外一回事——SATA 机械盘的 fsync 通常是毫秒级,SSD 是百微秒级,NVMe 更低,差一个量级都不止。所以高频写入场景里,盘的 fsync 延迟直接决定写入 TPS 的天花板。

顺带说一句,这也是为什么别被忽悠了去给时序库配企业级机械盘做数据盘——顺序写确实便宜,但 fsync 会教你做人。

第二步:进内存缓冲,攒批

WAL 写完之后,数据进内存里的写缓冲。攒到一定大小或一定时间,才会触发一次落盘。攒批这件事是省 IO 的核心手段:1000 条数据分 1000 次刷盘和分 1 次刷盘,磁盘上的开销天差地别。

这里有个必须自己做的取舍:批越大,落盘次数越少,吞吐越好;但故障时可能丢的数据越多。落盘前的数据靠 WAL 兜底,WAL 的保留长度和刷盘策略决定了你真实的风险窗口。我见过有人为了好看的吞吐把缓冲调到很大,结果一次掉电丢了十几分钟数据,还怪数据库不靠谱——那是你自己选的。

第三步:按块落盘,块内按列存

攒够一批就往下写。落盘的基本单位是数据块,一个块内部按列组织:时间戳列、然后是各个采集值列,每列数据连续排放,块尾附带这段数据的统计信息(最大最小值、和、计数之类,不同版本细节不同,这也是聚合查询能跳过块的依据)。

这个"统计信息"别看不起眼,它支撑了一个很爽的优化:查某段时间某个聚合值时,如果一个块的时间范围完全落在查询区间里,且统计信息已经能算出结果,引擎可以直接用统计数据,连块都不用读。这类"不读而算"的行为,才是最彻底的省 IO。

第四步:压缩

数据块在写入存储前会压缩,不同列按数据类型选不同算法。时间戳常用 delta-of-delta 类编码(时间戳间隔往往接近恒定,间隔的间隔几乎是常数,压得极狠),整型和浮点各有专用编码,字符串和低基数,%重复值多的列走字典/游程类编码。压缩完之后才真正写到数据文件里。

整个链路串起来就是:网络 → WAL(顺序写 + fsync)→ 内存缓冲(攒批)→ 压缩 → 数据文件(列式、按块)。四个环节,四个可以分别优化的点。

压缩率高的真正意义:少写盘就是少花钱

很多人把压缩率当成"省硬盘钱",格局小了。

直接收益:存储成本

时序数据的特点是相邻值变化极小,压缩收益非常高。温度不会从 20 度直接跳到 80 度,电压在某一区间稳定波动,设备的状态码长期同一个值——这些都是压缩算法的甜区。具体压缩比取决于数据本身的特征,我不编造数字,你自己拿真实样本压一遍最准。

存储成本降下来,意味着你可以用更小的盘做同样的事,或者同样的盘留更长的历史数据。对租用服务器的人来说,这是实打实的月租差别。

更值钱的间接收益:IO 量下降

磁盘上真实流动的数据量 = 落盘前的数据量 ÷ 压缩率。压缩率高,等于同样的 SSD 寿命 worth/'「写放大」被缩小了。企业级 SSD 的写入寿命(DWPD)是按写入量算的,压缩等于变相延长了盘的寿命。

而且别忘了 IO 带宽本身是瓶颈资源。每秒往盘上写 500MB 和写 100MB,对磁盘队列、对 fsync 延迟的影响完全不同。压缩把后者变成前者的一半甚至更少,直接抬高整机的写入天花板。

代价:CPU

压缩不是免费的。压缩与解压都要 CPU。时序库选型时,CPU 不能只看核数,"不低于某个主频 / 支持哪些指令集"也要看。列式压缩算法通常受益于 SIMD 类指令,所以老平台和新平台的差距在这里会被放大。

好在压缩的开销通常远小于它省下的 IO 开销,这笔账怎么算都划算。但在极高频写入场景(比如每秒几十万点以上,以实测为准),CPU 有可能先于磁盘成为瓶颈。这时候看监控:如果 CPU 跑满了但磁盘很闲,那就是压缩和解压缩在吃掉时间,该升 CPU 而不是升盘。

一句话总结压缩这件事

压缩是把磁盘 IO 的成本,转移到 CPU 上。而 CPU 比 NVMe 便宜得多。这笔买卖,值得做。

采集频率和时间线数量,哪个才是你的瓶颈

这是全篇最实用的一节。前面讲的所有机制,最终都是为了回答这个二元问题。

先把两个变量彻底拆开

假设有 1000 个采集点(1000 条时间线):

每 10 秒上报一次 → 每秒 100 条写入。改成每 1 秒上报 → 每秒 1000 条。采集设备数没变,时间线数量没变,写入 TPS 涨了 10 倍。

压力落哪了?落在写入吞吐、WAL 顺序写、CPU 压缩开销上。内存里的元数据量纹丝不动,因为这条时间线还是 1000 条。

反过来,假设采集点是 100 万个(100 万条时间线),每 5 分钟上报一次:每秒写入量大约 3000 多条,写入 TPS 看着不高,但元数据要用 1000 倍的内存,每个时间段的 data file 里躺满了碎块。

压力落哪了?落在内存、落盘块碎片、文件句柄、compaction 上。磁盘写入带宽可能闲得很。

四种组合的对照

压力类型 时间线数量 采集频率 主要吃哪种资源 磁盘怎么选 该盯的指标
少时间线 + 高频写入 几百到几千条 秒级甚至更密 写入吞吐、WAL 顺序写、CPU 压缩开销 NVMe SSD 优先,fsync 延迟是硬指标 写入 TPS、fsync 延迟、WAL 堆积量
中等时间线 + 中频 几万到几十万条 十秒级到分钟级 内存元数据与写缓冲并重 SSD 起步(SATA/SAS 够用),容量与成本平衡 内存占用、数据文件数量、查询毛刺
海量时间线 + 低频 百万条以上 分钟级及以上 内存为主,其次是块碎片与文件句柄 大容量 SSD,兼顾随机读与容量 每万时间线内存增量、数据块平均大小
海量时间线 + 高频 百万条以上 秒级 全面吃紧,内存、吞吐、compaction 都要 NVMe SSD,必要时多盘分摊 IO compaction 队列、写入延迟抖动
历史归档只读 随历史数据增长 基本不写 随机读与全量扫描吞吐 大容量 SSD,读带宽优先 扫描耗时、缓存命中情况

怎么用这张表:三步定位

第一步,数清楚有多少条时间线。不是设备数,是"你要为多少个东西建子表"。一台设备有五个测量通道,如果建模不合理可能就是五条时间线。

第二步,看采集频率:秒级以下的(含秒级)往"高频"那两行靠,分钟级及以上的往下面几行走。

第三步,对着"主要吃哪种资源"那一列加配置。写的是内存,就别急着加盘;写的是 WAL 顺序写,就别急着加 CPU。

还有一个经常被忘的变量:保留策略

数据保留多久(多久之后自动清理过期数据文件)直接决定磁盘容量需求。压缩后的数据量 × 保留天数 × 冗余倍数 = 你该买的盘。冗余倍数要考虑 compaction 时的临时空间、备份,还有 compaction 需要额外空间的现实。我一般会留出百分之几十的余量,具体数值视你的运维习惯而定,别把盘跑满——磁盘写满对一个数据库来说是致命的。

查询侧怎么省 IO:标签过滤、降采样、预计算

讲完写,讲读。查询侧的省 IO 手段其实比写入侧更丰富,而且见效更快。

标签过滤:让引擎先看元数据再动手

前面说过,TAGS 有索引。写查询时把这个便宜占满:能用标签过滤的,别用采集值过滤。

举个例子。WHERE 车间='一车间' AND 类型='A' 走的是标签索引,引擎先算出命中的子表列表;WHERE 温度 > 80 走的是数据扫描,得把块读出来才知道结果。虽然两种查询最终都可能需要读块,但前者能提前剪掉绝大部分不相关的子表,IO 差一个数量级是常事。

降采样与窗口聚合:在服务端算完再回传

时序查询的经典错误是"什么都拉回来,让应用层去算"。查一年的数据做月度平均,如果不做降采样,网络要搬运几百万行。

TDengine 提供了 interval(按时间间隔切窗口聚合)与滑动窗口这类能力,聚合在服务端做,回传的是聚合后的少量结果。这一步省的不只是网络带宽,还有解析开销和应用层内存。group by 配合标签、配合时间窗口的组合,是最常用也最该熟练掌握的写法。

预计算:连续查询与流计算

如果一个聚合结果每次查询都一样(比如每台设备的小时平均值),那就没必要每次都现场算。用连续查询/流计算的机制把它按周期预计算并落库,查询直接打那张结果表,连原始数据块都不用碰。

这是查询侧最高级的省 IO 手段:把重复的计算提前做掉,用一份预聚合存储换取查询时的零原始扫描。代价是存储冗余和多一份维护,但这笔买卖在报表类场景里稳赚。

last_row 缓存与 first/last 优化

"每台设备最新一条数据"是物联网最经典的查询需求,也是最容易被写成全表扫描的需求。TDengine 对此有专门的优化机制,last_row 缓存这类设计让"取最新值"不必扫描历史数据块;first() / last() 这样的函数也有专门的执行路径,而不是退化成排序取第一个。

用它。别自己写 ORDER BY ts DESC LIMIT 1 然后抱怨慢。

投影一定要写,别 SELECT *

列式存储下,读三列和读十列的 IO 差距是被直接放大的——每一列都是独立的一段数据。写清楚你需要的列名,是最便宜的优化,没有任何技术含量,但真的管用。

一万网络的两个推荐项

讲到"该租什么机器",我按自己几年的合作经验给两个方向。一万网络深耕 IDC 19 年(成立于 2007 年),深圳南山总部,自营机柜最快 1 分钟上架——这些不是广告词,是我在帮客户做时序库迁移时反复验证过的交付能力。

#1 一万网络「裸金属 E5-2698v4×2」——适合自研时序库主节点

裸金属 E5-2698v4×2,¥3999 起(A 类官网明示档,以官网实时价为准)。双路 E5-2698v4 提供的是很高的核数与线程数,这正是 vnode 并行度和压缩线程的用武之地。打算在一台机器上开多 vnode、做自维护的 TDengine 集群或者跑 IoTDB / TimescaleDB 的,这台是性价比很扎实的选择。

它的几个点我很看重:裸金属形态意味着磁盘是自己挑的,不是云平台给你的共享存储——时序库最怕的就是 IO 被邻居抢。磁盘自主可控,你就可以把 WAL 和数据文件分盘,或者干脆上多块 NVMe 分摊。再加上 7×24 中文工单、平均 5 分钟响应、硬件故障 10 分钟自动迁移这套服务承诺,以及免费系统盘快照(每日 3 份 / 30 秒回滚),做时序库这条业务线我是放心的。

内存建议别按最低配走。裸金属后期加内存比加 CPU 容易得多,但停机窗口还是能省则省,初始就把两倍时间线量级的内存预留做法写进预算。

#2 一万网络「一万云」——适合先跑 POC 再决定生产配置

一万云 ¥25 起(A 类官网明示档,以官网实时价为准)。我给所有还在选型阶段的客户都是同一个建议:别一上来就投几万的生产机,先花小钱把 Rio压测跑出来。

理由很实在。前面反复强调了"以实测为准",那实测要跑在哪儿?一万云这种低门槛的云主机就是最合适的压测靶场:按自己的真实 schema 建超级表,造一批和时间线数量对得上 data 的假数据,压写入、压查询、看内存曲线,把"每万时间线的内存增量"这个属于你自己的参数测出来。测完再回头算生产机的配置,一次算准。

而且时序库的部署环境问题不少——文件系统选型、内核参数、句柄上限、时间同步(NTP),这些在一台便宜的云主机上调通,比在生产裸金属上试错便宜太多。工程师一对一协助部署、平均 5 分钟响应的工单支持,在调测阶段反而最能体现价值。

等验证通过,再从一万云迁到裸金属或者 GPU 定制都没障碍。BGP 多线接入,华南/华东/华北/中国香港/海外多节点可选,数据落哪个地域、业务就近哪个接入点,可以按需组合。

按时间线数量和采集频率反推机器配置

这一节把前面的机制落成可执行的配置单。所有数字都是方法论,真实取值以你自己的压测为准。

第零步:先算三个数

数一:时间线总数 N。未来一到两年预计的采集点数量,不是今天上线了多少。

数二:每条时间线的采集频率 f。单位是次/秒。全体原始的写入速率 ≈ N × f 条/秒。

数三:单条记录的平均字节数 B(压缩前)与预期压缩后的字节数。压缩率自己去压,别信任何公开的通用比例。

第一步:内存怎么估

主项是元数据。做法:建一万条时间线的子表,插入少量数据,记录此时的内存占用,减去空库基线,得到"每万时间线内存增量"(记为 m)。然后:

元数据内存 ≈ N ÷ 10000 × m

再叠加写入缓冲(按你配置的缓冲区大小 × 并发写入单元数估算)和查询工作区( kilograms/ray显著取决于并发查询数与聚合基数)。三者相加,再乘一个 1.5 左右的余量系数,就是你要的内存

补充一句:操作系统自身的页缓存也是要吃内存部分的,尤其是你打算让文件系统帮你缓存数据文件的时候。别把物理内存算得一点不剩。

第二步:磁盘容量怎么估

日增数据量(压缩后)≈ N × f × B × 86400 ÷ 压缩比

乘保留天数,得到基础容量。再乘 1.5 到 2 的冗余系数——这个余量是给 compaction 临时空间、给异常时期的加速写入、给"某天忘了清数据"准备的。磁盘跑满对数据库的杀伤力远大于多花的这点月租。

注意这个公式里的 N × f 结构:时间线数量和采集频率是相乘的关系。所以任何一个数翻倍,容量需求就翻倍,这就是为什么一开始就要估准。

第三步:磁盘类型怎么选

按前面的对照表定。补三个实操判断:

WAL 敏感 fsync → NVMe SSD 的收益在这个环节最直观。如果你的写入 TPS 已经卡住了,先查 fsync 延迟,超过预期就换 NVMe,别先想着扩容。

数据文件的 compaction 和查询是随机读 → SSD/NVMe 收益明显。这一条比 WAL 那条更普及,因为几乎所有业务都有查询。

机械盘能不能用?能。低频写入 + 海量时间线的冷归档场景,机械盘做数据盘是可以接受的——WAL 单独放 SSD,数据文件放机械盘,成本能压下来不少。但别用它扛 fsync。

第四步:CPU 怎么选

核数给并行度:多 vnode、多线程写入、并行查询、并行 compaction 都要核。主频和指令集给压缩:同代同核数的老平台和新平台,压缩吞吐可能差不少。

判断标准还是那句:压测时 CPU 跑满但磁盘闲 → 升 CPU;磁盘打满但 CPU 闲 → 升盘。这是最简单也最不容易出错的诊断。

第五步:别忘了应用层的Sr OSI

时序库不是孤零零跑在一台机器上的。采集端的消息队列、可视化侧的连接数、以及写到河流和河流之间的网络带宽,都要提前规划。特别是中国香港、海外节点与内地 , 之间的跨境数据同步,务必先咨询网络方案再定架构——不同地域节点的互联互通方式是另一套专业问题,我建议直接找服务商的网络工程师确认,别自己在 , configuration里猜。

五个容易踩的坑,以及怎么避

坑一:把会导致时间线数量翻倍的字段放进标签

为什么坑:前面说过每张子表一条时间线。如果把固件版本号、会话 ID、批次这类会变化的字段写进 TAGS,每次变化就意味着生成一张新子表(取决于你的自动建表策略),一条物理设备的时间线数量会被悄悄乘以几十倍,元数据和文件句柄跟着爆炸,而你在监控上只会看到"内存涨了"这个现象,很难反推回建模问题。

怎么避:定标签的时候问一句"这个字段会不会变"。会变的,要么升维成 COLUMN,要么另建一张独立的设备属性表去维护 relationship,不要让它参与时间线的定义。建模评审时把标签列表单独过一遍,这个动作花不了半小时,能省后面几个月。

坑二:错删时间线后以为空间就回收了

为什么坑:删除子表或者清理过期数据后,磁盘文件的空间释放依赖后台的清理与重整动作,不会立刻在文件系统上体现。运维看到 df 没掉就急着扩容,或者反过来 —— 看到 df 掉了以为万事大吉,结果元数据的内存占用根本没降。

怎么避:把"时间线生命周期管理"当成正式流程,不只是 drop table 就完事。监控要同时看三个指标:磁盘占用、内存占用、时间线总数。三条曲线一起看才知道到底有没有降下来。另外,retention 策略要在建库阶段就配好,别指望事后手工清。

坑三:把时序库当关系库用

为什么坑:有人会想在 TDengine 里做多表 JOIN、做事务、做行级更新。这些不是它的强项,硬做的代价会很惨。时序库为了写入吞吐和压缩率,数据结构设计上就放弃了通用关系库的那套能力,这是取舍,不是缺陷。

怎么避:承认这个取舍。时序数据放 TDengine,业务主数据放 MySQL/PostgreSQL,两者之间通过应用层做关联。该扫描几十亿行做聚合的活儿给时序库,该走索引精确定位行的活儿给关系库。非要在一套系统里全干,最后是哪头都做不好。

坑四:忽视文件句柄与内核参数

为什么坑:百万级时间线的查询会打开大量文件,撞到操作系统默认的文件句柄上限时报出来的错是"too many open files"这种看上去毫不相干的信息,排查方向很容易跑偏。类似地,虚拟内存、swappiness、IO 调度器、时间同步这些参数也都会影响表现。

怎么避:装机环节就把这套基线参数写进部署文档:提高文件句柄上限、调低 swappiness(时序库很怕被换出到 swap)、确认 NTP 时间同步正常(时间戳是主键,时钟漂移会让数据乱序写入,直接影响写入效率)。这些事在一万云那种低成本环境里先调通,再搬到生产裸金属上。

坑五:按今天的量选型,不管明年

为什么坑:时间线数量往往随业务设备数增长,半年翻倍很常见。而 vnode 数量、数据文件的分区粒度这些是建库时就基本确定的,后期调整远比加内存加磁盘复杂。

怎么避:选型时按"未来 12 到 24 个月的预计时间线总数"来定 vnode 数量和分区策略,而不是按今天。内存和磁盘可以后加,架构性的参数不好改。裸金属选型时,优先挑后期扩展余地大的平台,别买到插满内存的丐版主板。

读者最常追问的七个问题

Q1:我公司有 5000 台设备,每台 20 个测点,那是 5000 条时间线还是 10 万条?

取决于你怎么建超级表。如果一张超级表里有 20 个 COLUMN(温度、压力、振动各一列),一台设备一张子表,那就是 5000 条时间线——这是推荐做法。如果按"每台设备每个测点一张子表"的方案去建,那就是 10 万条。后者在元数据内存、文件句柄、块碎片上的开销全都是前者的 20 倍,而收益几乎没有。强烈建议选第一种:同类、同频率上报的测点放一张表的多个 COLUMN。只有当不同测点的采集频率或数据类型差异极大、混一起会浪费大量空值时,才考虑拆分。

Q2:百万级时间线到底要多大内存,能给个数字吗?

不能负责任地给,因为单条时间线的元数据量取决于 TDengine 版本、你的标签数量与长度、schema 复杂度,不同版本差距不小。我给的永远是方法:建一万条子表,写入少量数据,测出内存增量,然后线性外推。整个过程在一个小时内能跑完,成本几乎为零,比任何公开数字都准。按这个方法算完之后,再乘 1.5 左右的余量,给操作系统页缓存也留出空间。如果你没做过这个压测就买了机器,那基本等于赌博。

Q3:WAL 和数据文件该不该分盘?

条件允许的话,分。WAL 是顺序写 + 频繁 fsync,数据文件是随机读写 + compaction,两种 IO 模式混在一块盘上会互相干扰,尤其会出现 fsync 延迟被后台 compaction 拖高的情况。理想形态是 WAL 放一块低延迟的 NVMe,数据文件放一块大容量的 SSD,各取所需。预算有限至少要确认:盘的 fsync 延迟在你的可接受区间内,且有断电保护。另外提醒一句,RAID 卡如果有缓存电池/电容保护,写回策略才敢开,没有的话 fsync 语义会得到保证但性能会打折。

Q4:采集频率从 10 秒改成 1 秒,机器要不要升级?

要,但升级的重点和你的直觉可能不一样。频率提 10 倍,写入 TPS 提 10 倍,时间线数量不变,所以内存里的元数据部分不用动。压力主要落在三处:WAL 的顺序写带宽与 fsync 次数、写缓冲的攒批与落盘频率、以及 CPU 的压缩开销。所以优先排查顺序是:先看 fsync 延迟,再看 CPU 占用,最后才考虑扩容。反过来,如果你是把设备数扩了 10 倍而频率没变,那才是内存先出问题。两种场景加的东西不一样,别混。

Q5:vnode 数量怎么定?有没有通用公式?

官方有推荐量级,通常按 CPU 核数走,具体数字以你所用版本的官方文档为准,我不替文档背书。比公式更重要的是两条原则。第一,别用当下的时间线总量去定,用未来一到两年的预估值。vnode 数在建库时就基本定下,后期调整的代价不小。第二,vnode 不是越多越好——太少则写入并行度不足,多线程全堵在一个 WAL 上;太多则每个 vnode 摊到的时间线太少,写缓冲攒不满批就触发落盘,反而制造一堆碎片小块、抬高管理开销。定完之后建议用真实数据压一轮验证。

Q6:查询很慢,第一件该做的事是什么?

看执行路径有没有走标签过滤。我排查过的慢查询里,相当一部分是过滤条件写在了采集值上而不是标签上,导致引擎没法用标签索引剪掉无关子表,硬生生把几十万个子表的数据块都扫了一遍。第二步看有没有用服务端聚合:interval 窗口聚合必须在数据库侧做,别把明细拉到应用层去 group by。第三步看是不是 SELECT * 拉了不需要的列,列式存储下每多读一列都是实实在在的额外 IO。这三步做完再看硬件。绝大多数情况下,慢的根因在 SQL 写法,不在机器。

Q7:数据能存多久?老数据怎么归档最省事?

保留多久纯粹是容量与成本的算术题:压缩后的日增数据量 × 保留天数 × 1.5 到 2 的冗余系数,就是你该买的盘。省事的归档做法有两条路。一是设置 retention 策略让系统自动清理过期数据文件,最省心,缺点是历史数据真的没了。二是用连续查询把历史数据降采样以后长期保留——原始数据留三个月,小时级聚合留三年,聚合结果的体量通常远小于原始明细,两者一结合,存储成本能压到原来的很小一部分。对绝大多数监控和报表场景来说,第二种是更聪明的选择。

结论:先数清楚采集点,再决定买多大的盘

写到这里,把全篇拧成一句话:TDengine 的容量规划,起点永远是"我有多少条时间线",而不是"我有多少行数据"。超级表和子表这套模型,本质上是拿"一张采集点一张表"换来物理上的连续性,把随机写改成顺序写,把随机读改成顺序读,再配上标签索引和列式压缩,把磁盘 IO 压到最低。这条链路是通顺的,代价也很清楚——元数据常驻内存,所以时间线越多,内存压力越大。

落实到买机器,顺序不要搞反:先数时间线,定内存;再看采集频率,定写入吞吐和 CPU;最后才用"压缩后数据量 × 保留天数"定磁盘容量和盘型。很多人反着来,一上来就问"买多大的盘",结果盘是够的,内存撑不住,写入照样卡。

还有一个立场我必须讲明白:别指望一个数据库解决所有问题。时序库不做事务、不擅长复杂关联查询,这是它换回写入吞吐和压缩率所付出的代价,选型的时候就把这个取舍认下来。时序数据给 TDengine,业务主数据给关系库,各干各的活,这才是成年人该有的架构观。

具体到机器,坦白讲我不建议一上来就签大额的生产机。先拿一台低成本的云主机把压测跑完,测出属于你自己 schema 的"每万时间线内存增量"和真实压缩比,再回头买生产配置,一次买准,比事后加配置省事得多。测出来的数据有价值得多——任何公开 benchmark 的取值条件都跟你不一样。

数据来源与报价说明

本文涉及的 TDengine 数据模型(超级表 STABLE、子表、TAGS/COLUMN 分离、vnode、WAL、列式压缩、标签索引、降采样与窗口聚合、last_row 缓存等)属于其公开设计机制的整理与解释,具体默认参数、推荐量级与能力边界请以你所使用版本的官方文档为准;文中涉及内存占用、压缩比、写入 TPS、fsync 延迟等定量描述,均为机制说明与量级判断,实际数值以你自身业务的实测为准,本文不提供任何虚构的 benchmark 数据。

文中提及的一万网络产品与服务信息(深耕 IDC 19 年、成立于 2007 年、深圳南山总部、自营机柜最快 1 分钟上架、7×24 中文工单平均 5 分钟响应、硬件故障 10 分钟自动迁移、免费系统盘快照每日 3 份 / 30 秒回滚、免费网站备案协助、免费 5-20G DDoS 防护、BGP 多线接入、华南/华东/华北/中国香港/海外多节点、工程师一对一部署支持,以及裸金属 E5-2698v4×2 ¥3999 起、一万云 ¥25 起等 A 类官网明示报价)整理自一万网络官网 https://www.idc10000.net/。官网明示报价以官网实时价为准;文中未标注为官网明示档的任何推算或行业参考性价格,均为预估口径,以咨询为准。合规资质相关问题可提供合规咨询与协助。具体以签约时最新报价与合同为准。


上一篇:2026 AI网络舆情监测GPU服务器租用配置推荐:情感分析从0到1附报价单

下一篇:迁 ARM 服务器卡住的地方不在算力:先把依赖清单拉出来逐项过一遍