2.数据模型 > 03 数据类型深度解析

适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-05-16
概述
TDengine 的数据类型体系为时序数据场景深度优化:支持从布尔到地理坐标的丰富类型,每种类型都有针对性的压缩编码算法,力求在存储效率和查询性能之间取得最优平衡。
本文涵盖:
- 所有支持的数据类型及其属性
- 各类型的存储编码与压缩机制
- 类型在列(Column)和标签(Tag)中的使用差异
- 数据类型选型最佳实践
核心概念速查表
| 概念 | 说明 |
|---|---|
| 定长类型 | 每个值占用固定字节(TINYINT、INT、FLOAT 等) |
| 变长类型 | 每个值长度不固定,带长度前缀(BINARY、NCHAR、JSON、VARBINARY、GEOMETRY) |
| TIMESTAMP | 第一列的强制类型,按数据库精度存储为 64 位整数 |
| NULL | 每种类型都支持 NULL,通过位图(Bitmap)标记 |
| 编码(Encoding) | 将原始值转为更紧凑的中间表示(如差分编码、字典编码) |
| 压缩(Compression) | 对编码后的数据块做通用压缩(如 LZ4、ZSTD) |
详细解析
1. 完整类型列表
1.1 数值类型
| 类型 | 大小 | 取值范围 | 说明 |
|---|---|---|---|
| TINYINT | 1 字节 | [-127, 127] | 有符号 8 位整数 |
| SMALLINT | 2 字节 | [-32767, 32767] | 有符号 16 位整数 |
| INT | 4 字节 | [-2147483647, 2147483647] | 有符号 32 位整数 |
| BIGINT | 8 字节 | [-9223372036854775807, 9223372036854775807] | 有符号 64 位整数 |
| TINYINT UNSIGNED | 1 字节 | [0, 254] | 无符号 8 位整数 |
| SMALLINT UNSIGNED | 2 字节 | [0, 65534] | 无符号 16 位整数 |
| INT UNSIGNED | 4 字节 | [0, 4294967294] | 无符号 32 位整数 |
| BIGINT UNSIGNED | 8 字节 | [0, 18446744073709551614] | 无符号 64 位整数 |
| FLOAT | 4 字节 | IEEE 754 单精度 | 约 6~7 位有效数字 |
| DOUBLE | 8 字节 | IEEE 754 双精度 | 约 15~16 位有效数字 |
注意:各整数类型的最大/最小值比标准 C 类型少 1,因为 TDengine 保留了极值作为 NULL 的内部表示。
1.2 布尔类型
| 类型 | 大小 | 取值 | 说明 |
|---|---|---|---|
| BOOL | 1 字节 | TRUE / FALSE / NULL | 布尔值,内部存储为 TINYINT(0/1) |
1.3 时间戳类型
| 类型 | 大小 | 说明 |
|---|---|---|
| TIMESTAMP | 8 字节 | 64 位有符号整数,含义取决于数据库精度 |
时间戳的实际含义:
| 精度 | 含义 | 示例值 | 时间范围 |
|---|---|---|---|
| ms | 自 1970-01-01 的毫秒数 | 1705312800000 | 1970 ~ 2286 年 |
| us | 自 1970-01-01 的微秒数 | 1705312800000000 | 1970 ~ 2286 年 |
| ns | 自 1970-01-01 的纳秒数 | 1705312800000000000 | 1970 ~ 2262 年 |
sql
-- 时间戳的多种写法
INSERT INTO d1001 VALUES
('2024-01-15 10:00:00.000', 10.3, 219), -- 字符串格式
(1705312800000, 10.5, 220), -- 整数格式(ms 精度)
(NOW, 10.8, 221); -- 特殊关键字
1.4 字符串/二进制类型
| 类型 | 最大长度 | 说明 |
|---|---|---|
| BINARY(N) | N ∈ [1, 65517] 字节 | 定义长度为 N 的字节串(ASCII 字符串) |
| VARCHAR(N) | 同 BINARY | BINARY 的别名 |
| NCHAR(N) | N ∈ [1, 16379] 字符 | Unicode 字符串,每字符占 4 字节(UTF-32) |
| VARBINARY(N) | N ∈ [1, 65517] 字节 | 任意二进制数据 |
BINARY vs NCHAR:
| 对比 | BINARY | NCHAR |
|---|---|---|
| 编码 | 单字节(ASCII/Latin1) | 4 字节/字符(UCS-4) |
| 中文 | 不支持 | 支持 |
| 存储效率 | 高(纯英文场景) | 低(固定 4 字节/字符) |
| 最大字符数 | 65517 字节 | 16379 字符 |
选型建议:如果数据只包含 ASCII 字符(英文、数字、符号),使用 BINARY 更省空间。如果可能包含中文或其他 Unicode 字符,必须使用 NCHAR。
1.5 特殊类型
| 类型 | 大小 | 说明 |
|---|---|---|
| JSON | 变长,最大 4096 字节 | JSON 格式字符串,仅用于 Tag |
| GEOMETRY(N) | 变长,N ∈ [1, 65517] 字节 | WKB 格式的地理几何数据 |
2. 类型在列和 Tag 中的使用
| 类型 | 可作列(Column) | 可作标签(Tag) |
|---|---|---|
| TIMESTAMP | ✅(第一列必须是) | ✅ |
| 所有整数类型 | ✅ | ✅ |
| FLOAT / DOUBLE | ✅ | ✅ |
| BOOL | ✅ | ✅ |
| BINARY / NCHAR | ✅ | ✅ |
| VARBINARY | ✅ | ✅ |
| GEOMETRY | ✅ | ✅ |
| JSON | ❌ | ✅(仅 Tag,且一张超级表只能有一个 JSON Tag) |
JSON Tag 的特殊性:
- 一张超级表只能定义一个 JSON 类型的 Tag
- JSON Tag 不能与其他 Tag 共存
- 支持通过
->操作符和函数查询 JSON 内部字段
sql
-- JSON Tag 示例
CREATE STABLE device_info (
ts TIMESTAMP,
value DOUBLE
) TAGS (
info JSON -- 唯一 Tag,不能再加其他 Tag
);
-- 写入
CREATE TABLE dev01 USING device_info TAGS ('{"model":"X100","firmware":"2.1.3","region":"east"}');
-- 查询 JSON 字段
SELECT * FROM device_info WHERE info->'region' = 'east';
3. 存储编码与压缩
3.1 两层压缩架构
TDengine 对数据块采用两层压缩策略:
数据压缩流水线:
原始数据 → 一阶编码(类型特化) → 二阶压缩(通用算法) → 磁盘
一阶编码(COMP ≥ 1 时启用):
根据数据类型选择最优编码方式
目标:消除数据的规律性和冗余
二阶压缩(COMP = 2 时启用):
对编码后的字节流做通用压缩
目标:进一步压缩剩余的熵
3.2 各类型的编码策略
| 类型 | 一阶编码方式 | 原理 |
|---|---|---|
| TIMESTAMP | Delta-of-Delta | 时间戳等间隔时差值的差值接近 0,极小整数变长编码 |
| INT / BIGINT | ZigZag + Simple8B | 有符号转无符号(ZigZag)+ 位打包(多值共享字) |
| FLOAT | XOR 差分 | 相邻浮点值 XOR 后前导零和尾部零多,紧凑存储非零位 |
| DOUBLE | XOR 差分 | 同 FLOAT |
| BOOL | 位打包 | 8 个布尔值压入 1 字节 |
| BINARY/NCHAR | 字典编码 / 前缀压缩 | 重复值引用字典,相似前缀共享 |
| TINYINT/SMALLINT | Simple8B | 小整数紧凑打包 |
3.3 TIMESTAMP 的特殊编码
时间戳列的压缩效果最为显著,因为时序数据通常以固定频率采集:
TIMESTAMP Delta-of-Delta 编码示例:
原始时间戳(ms):
1705312800000, 1705312801000, 1705312802000, 1705312803000, 1705312804000
一阶差分(Delta):
1000, 1000, 1000, 1000
二阶差分(Delta of Delta):
0, 0, 0
编码后:只需存储基准值 + 少量修正位
压缩率:100:1 级别(等间隔采集时)
3.4 NULL 的存储
每个数据块维护一个 NULL 位图(Bitmap):
NULL 位图机制:
数据块:100 行数据
位图大小:⌈100/8⌉ = 13 字节
位图中:
bit = 0 → 对应行有值(从数据区读取)
bit = 1 → 对应行为 NULL(不占数据区空间)
优势:
- NULL 几乎不占存储空间
- 判断 NULL 只需位运算,极快
3.5 压缩率参考
| 数据场景 | COMP=1 压缩率 | COMP=2 压缩率 |
|---|---|---|
| 等频时间戳 | 50:1 ~ 100:1 | 100:1+ |
| 整数计数器(单调递增) | 10:1 ~ 20:1 | 20:1 ~ 50:1 |
| 浮点传感器值(缓变) | 3:1 ~ 5:1 | 5:1 ~ 10:1 |
| 浮点传感器值(剧变) | 2:1 ~ 3:1 | 3:1 ~ 5:1 |
| 字符串(低基数) | 5:1 ~ 20:1 | 10:1 ~ 50:1 |
| 字符串(高基数) | 1.5:1 ~ 3:1 | 2:1 ~ 5:1 |
| 布尔值 | 8:1 | 8:1+ |
4. 类型转换规则
4.1 隐式转换
TDengine 在表达式计算和比较时支持有限的隐式类型转换:
| 场景 | 规则 |
|---|---|
| 整数 ↔ 浮点运算 | 整数提升为浮点 |
| 不同宽度整数运算 | 窄类型提升为宽类型 |
| 时间戳与字符串比较 | 字符串尝试解析为时间戳 |
| 数值与字符串比较 | 不允许,返回错误 |
4.2 显式转换
sql
-- CAST 语法
SELECT CAST(voltage AS FLOAT) FROM meters;
SELECT CAST(ts AS BIGINT) FROM meters; -- 转为整数时间戳
SELECT CAST(1705312800000 AS TIMESTAMP) FROM meters;
5. GEOMETRY 类型详解
GEOMETRY 类型支持存储地理空间数据,采用 WKB(Well-Known Binary)格式:
sql
-- 创建带 GEOMETRY 列的表
CREATE STABLE geo_devices (
ts TIMESTAMP,
temperature FLOAT,
location GEOMETRY(256)
) TAGS (region BINARY(64));
-- 写入(使用 WKT 文本格式,系统自动转为 WKB 存储)
INSERT INTO dev01 USING geo_devices TAGS ('east')
VALUES (NOW, 25.6, 'POINT(116.397128 39.916527)');
-- 空间查询
SELECT * FROM geo_devices
WHERE ST_Contains(
ST_GeomFromText('POLYGON((116.0 39.0, 117.0 39.0, 117.0 40.0, 116.0 40.0, 116.0 39.0))'),
location
);
支持的几何类型:
- POINT:点
- LINESTRING:线段
- POLYGON:多边形
6. 列数限制与行宽限制
| 限制项 | 值 |
|---|---|
| 最大列数(含时间戳列) | 4096 |
| 最大 Tag 数 | 128 |
| 单行最大宽度 | 65531 字节(所有定长列 + 变长列实际值) |
| 单个 BINARY/NCHAR/VARBINARY 最大长度 | 65517 字节 |
| 所有 Tag 总大小 | 16384 字节 |
行宽计算:
行总宽度 = Σ(各定长列大小) + Σ(各变长列实际值大小 + 2字节长度前缀)
示例:
INT(4) + FLOAT(4) + BINARY(100实际用30) + NCHAR(50实际用20字符)
= 4 + 4 + (2+30) + (2+80) = 122 字节
代码示例
各类型的使用示例
sql
-- 创建包含多种类型的超级表
CREATE STABLE sensor_data (
ts TIMESTAMP, -- 必须的时间戳列
temp FLOAT, -- 温度(浮点)
humidity TINYINT UNSIGNED, -- 湿度百分比 [0,100]
pressure DOUBLE, -- 精密压力值
status BOOL, -- 设备状态
error_code SMALLINT, -- 错误码
raw_data VARBINARY(1024), -- 原始二进制数据
memo NCHAR(200), -- 备注(支持中文)
location GEOMETRY(128) -- GPS 位置
) TAGS (
device_id BINARY(32), -- 设备编号
model BINARY(16), -- 型号
install_date TIMESTAMP, -- 安装日期
floor INT -- 楼层
);
类型选择的权衡
sql
-- ❌ 不推荐:用 DOUBLE 存储只有 0~100 范围的整数值
CREATE STABLE bad_design (
ts TIMESTAMP,
percentage DOUBLE -- 浪费:8 字节存储 [0,100] 的值
) TAGS (id INT);
-- ✅ 推荐:用最小满足需求的类型
CREATE STABLE good_design (
ts TIMESTAMP,
percentage TINYINT UNSIGNED -- 节省:1 字节存储 [0,254] 的值
) TAGS (id INT);
-- 存储节省:每行省 7 字节,1亿行节省 700MB(压缩前)
性能考量
类型对存储的影响
| 类型选择 | 每值大小 | 1 亿行存储(COMP=2 约) |
|---|---|---|
| TINYINT | 1 字节 | ~5 MB |
| INT | 4 字节 | ~20 MB |
| BIGINT | 8 字节 | ~40 MB |
| FLOAT | 4 字节 | ~30 MB |
| DOUBLE | 8 字节 | ~50 MB |
| BINARY(32) 平均用 16 | ~18 字节 | ~200 MB |
| NCHAR(32) 平均用 10 字符 | ~42 字节 | ~500 MB |
类型对查询性能的影响
| 因素 | 影响 |
|---|---|
| 定长类型 | 可直接按偏移定位,列扫描最快 |
| 变长类型 | 需要解析长度前缀,列扫描稍慢 |
| 窄类型(TINYINT) | 更多值可缓存在 CPU 缓存行中 |
| 宽类型(NCHAR) | 可能跨缓存行,吞吐较低 |
最佳实践总结
| 建议 | 说明 |
|---|---|
| 选择最窄的满足需求的类型 | 减少存储、提升缓存命中率 |
| 优先 BINARY 而非 NCHAR | 除非必须存储非 ASCII 字符 |
| 变长类型长度设为实际最大值 | 不浪费存储,但设太小会截断 |
| 布尔值用 BOOL 不用 INT | 压缩率 8:1 vs 约 5:1 |
| 等频采集数据不需特殊处理 | TIMESTAMP Delta-of-Delta 自动获得极高压缩率 |
FAQ
Q1: BINARY(100) 如果只写入 10 字节会占用 100 字节空间吗?
不会。BINARY 是变长存储,只存实际写入的字节加 2 字节长度前缀。定义时的 N 只是最大上限约束。
Q2: FLOAT 和 DOUBLE 怎么选?
- FLOAT(4 字节):约 6~7 位有效数字,适合温度(25.3)、电流(10.5A)等精度需求不高的传感器值
- DOUBLE(8 字节):约 15~16 位有效数字,适合经纬度(116.397128)、金融价格等需要高精度的场景
- 如果精度需求在 FLOAT 范围内,选 FLOAT 可节省约 50% 存储
Q3: 为什么整数类型的范围比标准的少 1?
TDengine 保留了每种整数类型的极值(如 INT 的 -2147483648)作为 NULL 的内部哨兵值。写入时如果值等于保留值,会被当作 NULL 处理。这使得 NULL 检测只需简单的值比较,无需额外元数据。
Q4: JSON Tag 能建索引吗?
JSON Tag 不支持传统索引,但支持通过 -> 操作符进行条件过滤。由于 Tag 数据量通常不大(存储在内存中),JSON 字段的过滤性能在大多数场景下仍可接受。如果对查询性能有极致要求,建议使用独立的普通 Tag 代替 JSON。
Q5: GEOMETRY 数据量大会影响性能吗?
GEOMETRY 列按 WKB 格式存储,复杂多边形可能占用数百字节。建议:
- 简单点位数据用 2 个 DOUBLE 列(经度 + 纬度)代替,存储更紧凑
- 只有需要空间查询(
ST_Contains、ST_Intersects)时才使用 GEOMETRY 类型 - 控制 GEOMETRY 列的长度上限,避免存储过大的多边形
Q6: 数据库精度选 ms、us 还是 ns?
| 场景 | 推荐精度 | 原因 |
|---|---|---|
| 工业物联网(采集频率 ≤ 1kHz) | ms | 毫秒足够区分每个采样点 |
| 金融行情(Level 2 快照) | us | 微秒可区分同一毫秒内的多个报价 |
| 电力 PMU / 科学实验 | ns | 需要纳秒级时间分辨率 |
三种精度的存储大小相同(都是 8 字节),性能无差异。区别仅在于时间戳数值的解释方式和可表示的时间范围。
参考
系统构架篇
- 01-《TDengine 整体架构全景》
- 02-《集群拓扑深度解析》
- 03-《MNode 内部机制深度解析》
- 04-《RPC 通信层深度解析》
- 05-《VNode 生命周期》
- 06-《RAFT 共识协议》
- 07-《端到端的消息流》
数据模型
- 01-《数据库创建与参数详解》
- 02-《超级表/子表/普通表》
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。