TDengine 支持数据类型深度解析 — 类型体系、存储编码与选型指南

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

在这里插入图片描述

适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-05-16

概述

TDengine 的数据类型体系为时序数据场景深度优化:支持从布尔到地理坐标的丰富类型,每种类型都有针对性的压缩编码算法,力求在存储效率和查询性能之间取得最优平衡。

本文涵盖:

  1. 所有支持的数据类型及其属性
  2. 各类型的存储编码与压缩机制
  3. 类型在列(Column)和标签(Tag)中的使用差异
  4. 数据类型选型最佳实践

核心概念速查表

概念说明
定长类型每个值占用固定字节(TINYINT、INT、FLOAT 等)
变长类型每个值长度不固定,带长度前缀(BINARY、NCHAR、JSON、VARBINARY、GEOMETRY)
TIMESTAMP第一列的强制类型,按数据库精度存储为 64 位整数
NULL每种类型都支持 NULL,通过位图(Bitmap)标记
编码(Encoding)将原始值转为更紧凑的中间表示(如差分编码、字典编码)
压缩(Compression)对编码后的数据块做通用压缩(如 LZ4、ZSTD)

详细解析

1. 完整类型列表

1.1 数值类型
类型大小取值范围说明
TINYINT1 字节[-127, 127]有符号 8 位整数
SMALLINT2 字节[-32767, 32767]有符号 16 位整数
INT4 字节[-2147483647, 2147483647]有符号 32 位整数
BIGINT8 字节[-9223372036854775807, 9223372036854775807]有符号 64 位整数
TINYINT UNSIGNED1 字节[0, 254]无符号 8 位整数
SMALLINT UNSIGNED2 字节[0, 65534]无符号 16 位整数
INT UNSIGNED4 字节[0, 4294967294]无符号 32 位整数
BIGINT UNSIGNED8 字节[0, 18446744073709551614]无符号 64 位整数
FLOAT4 字节IEEE 754 单精度约 6~7 位有效数字
DOUBLE8 字节IEEE 754 双精度约 15~16 位有效数字

注意:各整数类型的最大/最小值比标准 C 类型少 1,因为 TDengine 保留了极值作为 NULL 的内部表示。

1.2 布尔类型
类型大小取值说明
BOOL1 字节TRUE / FALSE / NULL布尔值,内部存储为 TINYINT(0/1)
1.3 时间戳类型
类型大小说明
TIMESTAMP8 字节64 位有符号整数,含义取决于数据库精度

时间戳的实际含义:

精度含义示例值时间范围
ms自 1970-01-01 的毫秒数17053128000001970 ~ 2286 年
us自 1970-01-01 的微秒数17053128000000001970 ~ 2286 年
ns自 1970-01-01 的纳秒数17053128000000000001970 ~ 2262 年
-- 时间戳的多种写法
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)同 BINARYBINARY 的别名
NCHAR(N)N ∈ [1, 16379] 字符Unicode 字符串,每字符占 4 字节(UTF-32)
VARBINARY(N)N ∈ [1, 65517] 字节任意二进制数据

BINARY vs NCHAR

对比BINARYNCHAR
编码单字节(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 内部字段
-- 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 各类型的编码策略
类型一阶编码方式原理
TIMESTAMPDelta-of-Delta时间戳等间隔时差值的差值接近 0,极小整数变长编码
INT / BIGINTZigZag + Simple8B有符号转无符号(ZigZag)+ 位打包(多值共享字)
FLOATXOR 差分相邻浮点值 XOR 后前导零和尾部零多,紧凑存储非零位
DOUBLEXOR 差分同 FLOAT
BOOL位打包8 个布尔值压入 1 字节
BINARY/NCHAR字典编码 / 前缀压缩重复值引用字典,相似前缀共享
TINYINT/SMALLINTSimple8B小整数紧凑打包
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:1100:1+
整数计数器(单调递增)10:1 ~ 20:120:1 ~ 50:1
浮点传感器值(缓变)3:1 ~ 5:15:1 ~ 10:1
浮点传感器值(剧变)2:1 ~ 3:13:1 ~ 5:1
字符串(低基数)5:1 ~ 20:110:1 ~ 50:1
字符串(高基数)1.5:1 ~ 3:12:1 ~ 5:1
布尔值8:18:1+

4. 类型转换规则

4.1 隐式转换

TDengine 在表达式计算和比较时支持有限的隐式类型转换:

场景规则
整数 ↔ 浮点运算整数提升为浮点
不同宽度整数运算窄类型提升为宽类型
时间戳与字符串比较字符串尝试解析为时间戳
数值与字符串比较不允许,返回错误
4.2 显式转换
-- 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)格式:

-- 创建带 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 字节

代码示例

各类型的使用示例

-- 创建包含多种类型的超级表
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                        -- 楼层
);

类型选择的权衡

-- ❌ 不推荐:用 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 约)
TINYINT1 字节~5 MB
INT4 字节~20 MB
BIGINT8 字节~40 MB
FLOAT4 字节~30 MB
DOUBLE8 字节~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_ContainsST_Intersects)时才使用 GEOMETRY 类型
  • 控制 GEOMETRY 列的长度上限,避免存储过大的多边形

Q6: 数据库精度选 ms、us 还是 ns?

场景推荐精度原因
工业物联网(采集频率 ≤ 1kHz)ms毫秒足够区分每个采样点
金融行情(Level 2 快照)us微秒可区分同一毫秒内的多个报价
电力 PMU / 科学实验ns需要纳秒级时间分辨率

三种精度的存储大小相同(都是 8 字节),性能无差异。区别仅在于时间戳数值的解释方式和可表示的时间范围。

参考

系统构架篇

数据模型

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TDengine (老段)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值