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 数值类型
类型 大小 取值范围 说明
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_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 提供实时分析、可视化、事件管理与报警等功能。

相关推荐
恣艺5 小时前
用Go从零实现一个高性能KV存储引擎:B+Tree索引、WAL持久化、LRU缓存的工程实践
开发语言·数据库·redis·缓存·golang
清平乐的技术专栏6 小时前
【Flink学习】(四)Flink 常用转换算子,数据流数据处理
大数据·flink
浮尘笔记7 小时前
Java Snowy框架CI/CD云效自动化部署流程
java·运维·服务器·阿里云·ci/cd·自动化
weelinking14 小时前
【产品】00_产品经理用Claude实现产品系列介绍
数据库·人工智能·sql·数据挖掘·github·产品经理
一直不明飞行14 小时前
Java的equals(),hashCode()应该在什么时候重写
java·开发语言·jvm
REDcker14 小时前
有限状态机与状态模式详解 FSM建模Java状态模式与C++表驱动模板实践
java·c++·状态模式
2301_8039346114 小时前
Go语言如何做网络爬虫_Go语言爬虫开发教程【指南】
jvm·数据库·python
你的保护色14 小时前
【无标题】
java·服务器·网络
basketball61615 小时前
C++ 构造函数完全指南:从入门到进阶
java·开发语言·c++