物联网监控场景时序数据库选型:从 InfluxDB 迁移到 TDengine 的实战复盘
1. 背景:设备数据量翻倍,查询开始"卡脖子"
2025 年初,我们团队负责某智慧园区 IoT 监控平台的后端改造。平台接入园区内 1.2 万个传感器(电表、水表、温湿度、烟感、门禁),每 5 秒上报一次数据,日均写入量约 2 亿条。原有方案是 MySQL 分库分表 + Redis 缓存最新值,勉强撑到 2024 年底。随着园区二期扩建,设备数预计增至 2.5 万,日均写入将突破 4 亿条。
压测时问题集中爆发:单条最新值查询 P99 从 80ms 涨到 1.2s,按设备+时间范围聚合(如"某栋楼过去 24 小时平均温度")的接口 P99 超过 3s,前端大屏频繁加载中。更麻烦的是,MySQL 单表数据量过亿后,夜间批量清理历史数据的 DELETE 任务经常锁表,导致写入抖动。
我们意识到,关系型数据库做时序数据存储,在写入吞吐、压缩比和聚合查询上已经到瓶颈。团队决定引入专业时序数据库,在 InfluxDB 和 TDengine 之间做选型。
2. 踩坑:MySQL 撑不住的真实瓶颈
先看数据特征:传感器数据是典型的"写多读少、按时间追加、极少更新"的时序数据。MySQL 的 B+ 树索引在写入时需维护随机索引,写入吞吐上不去;数据无压缩,1.2 万设备×每天 2 亿条,单日原始数据约 40GB,存储成本高;聚合查询要走全表扫描或二级索引回表,延迟不可控。
我们用 2000 万条真实数据做了基准测试,结论如下:
| 指标 | MySQL 8.0(分库分表) | InfluxDB 2.7 | TDengine 3.2 |
|---|---|---|---|
| 写入吞吐(万条/秒) | 3.2 | 18.5 | 42.7 |
| 1 小时聚合查询 P99 | 2.8s | 620ms | 180ms |
| 数据压缩比 | 1:1(无压缩) | 1:3.8 | 1:7.2 |
| 单机存储 30 天数据 | 约 1.2TB | 约 320GB | 约 170GB |
MySQL 在写入和压缩上全面落后,这是根因。接下来进入选型对比。
3. 方案:选型、调参与改造
3.1 方案 A:InfluxDB 2.7
InfluxDB 是时序数据库的老牌选手,生态成熟,Flux 查询语言功能强,自带数据保留策略(RP)和连续查询(CQ)。我们先用它做了 PoC,写入和查询都达标,但遇到两个问题:
- 资源占用高:单机 8C16G 部署,写入 18 万条/秒时 CPU 持续 85% 以上,内存占用 12GB,接近上限。
- 集群版成本高:开源版是单机架构,高可用需自建;官方集群版按节点收费,对我们这种预算有限的团队不友好。
3.2 方案 B:TDengine 3.2
TDengine 是国产时序数据库,核心设计是"一个设备一张表"(超级表 + 子表),数据按时间分片存储,写入和聚合性能突出。PoC 结果:
- 单机 8C16G 写入 42 万条/秒,CPU 稳定在 60% 左右;
- 聚合查询走列式存储 + 预计算,P99 180ms;
- 压缩比 1:7.2,30 天数据仅占 170GB;
- 开源版支持多副本,满足高可用需求。
3.3 选型结论
我们最终选择 TDengine 3.2,核心依据有三点:写入吞吐高出一倍多、压缩比省存储成本、开源版自带多副本。InfluxDB 适合查询语法复杂、需要 Flux 灵活分析的场景,但我们核心诉求是"高吞吐写入 + 按时间聚合",TDengine 更匹配。
4. 实操:从 InfluxDB 迁移到 TDengine
4.1 环境与版本
# 服务端:TDengine 3.2.3.0(Linux x86_64)
# 客户端:taos-jdbcdriver 3.2.7
# 连接器:taos-connector-python 3.1.0
# 部署方式:3 节点集群(每节点 8C16G,SSD)
4.2 建库建表
-- 创建数据库,保留 30 天数据
CREATE DATABASE iot_monitor KEEP 30 DURATION 10 BUFFER 256 WAL_LEVEL 2;
-- 创建超级表(按设备类型建模)
CREATE STABLE meters (
ts TIMESTAMP,
value FLOAT,
status INT
) TAGS (device_id NCHAR(32), building_id NCHAR(16), type NCHAR(16));
-- 子表由程序自动创建,例如:
CREATE TABLE meters_0001 USING meters TAGS ('dev_0001', 'B01', 'electricity');
关键点:DURATION 10 表示每 10 天一个数据分片,KEEP 30 自动清理 30 天前的数据,省去手工 DELETE。
4.3 写入代码(Java 示例)
// pom.xml 依赖
// <dependency>
// <groupId>com.taosdata.jdbc</groupId>
// <artifactId>taos-jdbcdriver</artifactId>
// <version>3.2.7</version>
// </dependency>
String jdbcUrl = "jdbc:TAOS-RS://192.168.1.10:6041/iot_monitor";
try (Connection conn = DriverManager.getConnection(jdbcUrl, "root", "taosdata")) {
// 批量写入,每批 1000 条
String sql = "INSERT INTO meters_0001 USING meters TAGS (?, ?, ?) VALUES (?, ?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
for (SensorData d : batch) {
ps.setString(1, d.deviceId);
ps.setString(2, d.buildingId);
ps.setString(3, d.type);
ps.setTimestamp(4, d.ts);
ps.setFloat(5, d.value);
ps.setInt(6, d.status);
ps.addBatch();
}
ps.executeBatch(); // 预期:1000 条/批,写入耗时约 20ms
}
}
4.4 聚合查询
-- 某栋楼过去 24 小时平均温度,按小时聚合
SELECT _wstart AS hour, AVG(value) AS avg_temp
FROM meters
WHERE building_id = 'B01' AND type = 'temperature'
AND ts >= NOW() - 24h
PARTITION BY _wstart
INTERVAL(1h);
预期结果:返回 24 行,每行一个小时的均值,P99 查询耗时约 180ms。
5. 踩坑与排错
5.1 坑一:时区导致聚合结果偏移
报错/现象:按小时聚合时,发现 _wstart 的时间戳比预期早 8 小时。
排查:TDengine 默认使用 UTC 时区,而业务数据是北京时间(UTC+8)。聚合窗口按 UTC 切分,导致窗口边界偏移。
解决:建库时指定时区:
CREATE DATABASE iot_monitor KEEP 30 DURATION 10 BUFFER 256 WAL_LEVEL 2
TIMEZONE 'Asia/Shanghai';
5.2 坑二:批量写入偶发 Out of memory 错误
报错:
TDengine ERROR (245): Out of memory
排查:查看 taosd 日志,发现 BUFFER 参数设置过小,写入高峰时内存缓冲不足。同时客户端批量大小 5000 条过大,单次提交数据量超限。
解决:将 BUFFER 从 256 调到 512,客户端批量降到 1000 条/批,问题消失。
5.3 坑三:子表数量过多导致建表慢
现象:设备从 1.2 万增至 2.5 万后,首次建子表耗时从 5 分钟增至 20 分钟。
排查:每个设备一张子表,2.5 万张子表在单次事务里创建,元数据写入压力大。
解决:改为程序分批创建,每批 500 张,间隔 100ms;同时用 CREATE TABLE IF NOT EXISTS 避免重复建表报错。
6. 验证数据与效果## 6. 验证数据与效果
迁移完成后,我们对线上流量做了 72 小时压测和灰度验证:
| 指标 | 迁移前(MySQL) | 迁移后(TDengine) | 提升 |
|---|---|---|---|
| 写入吞吐 | 3.2 万条/秒 | 42 万条/秒 | 13 倍 |
| 最新值查询 P99 | 1.2s | 45ms | 26 倍 |
| 24h 聚合查询 P99 | 3s+ | 180ms | 16 倍 |
| 30 天存储占用 | 约 1.2TB | 约 170GB | 省 86% |
| 夜间清理任务 | 锁表抖动 | 自动分片淘汰 | 无感 |
线上灰度一周,无写入丢失,查询接口全部达标。大屏刷新从"加载中 3 秒"变成"秒开"。
7. 复盘:什么场景该用 / 不该用
适合用 TDengine 的场景:
- 设备/传感器数量大(万级+),写入频率高(秒级);
- 查询以"按时间范围聚合"为主,如均值、最大值、计数;
- 数据有保留期限,需要自动清理;
- 预算有限,希望开源方案自带高可用。
不适合用 TDengine 的场景:
- 查询需要多表 JOIN 或复杂事务,TDengine 的 SQL 能力弱于关系型数据库;
- 数据更新频繁(时序数据极少更新,若业务需要频繁改历史值,不适合);
- 需要 Flux 这类灵活分析语法,InfluxDB 更合适;
- 数据量极小(日均百万条以下),MySQL/PostgreSQL 足够,引入时序库反而增加运维成本。
InfluxDB 的定位:如果团队已有 InfluxDB 运维经验,且查询分析需求复杂(如连续查询、降采样、告警规则),InfluxDB 仍是可靠选择;但要注意开源版单机限制和集群版成本。
最后提醒:选型没有银弹。我们踩过的坑——时区、内存参数、子表数量——都是文档里写得清楚但容易被忽略的细节。建议任何团队在迁移前,先用真实数据量做 PoC,重点测写入吞吐、聚合 P99 和压缩比,再决定是否切换。
更多推荐



所有评论(0)