目录
[一、TDengine 是什么?](#一、TDengine 是什么?)
[2.1 taosd --- 主服务进程](#2.1 taosd — 主服务进程)
[2.2 taos --- 原生命令行客户端](#2.2 taos — 原生命令行客户端)
[2.3 超级表(STable / Super Table)](#2.3 超级表(STable / Super Table))
[2.4 VNode(虚拟节点)](#2.4 VNode(虚拟节点))
[2.5 MNode(管理节点)](#2.5 MNode(管理节点))
[2.6 QNode(查询节点)](#2.6 QNode(查询节点))
[2.7 时间分区](#2.7 时间分区)
[四、实战案例:监控 100 台服务器 CPU 使用率](#四、实战案例:监控 100 台服务器 CPU 使用率)
[4.1 安装 TDengine](#4.1 安装 TDengine)
[4.2 创建数据库和超级表](#4.2 创建数据库和超级表)
[4.3 创建子表(每台服务器一张)](#4.3 创建子表(每台服务器一张))
[4.4 写入数据](#4.4 写入数据)
[4.5 查询数据(各种场景)](#4.5 查询数据(各种场景))
[4.6 Java(Spring Boot)完整集成](#4.6 Java(Spring Boot)完整集成)
[4.7 对接 Grafana 可视化](#4.7 对接 Grafana 可视化)
[4.8 对接 Prometheus(面试加分项)](#4.8 对接 Prometheus(面试加分项))
[五、面试高频题 & 参考答案](#五、面试高频题 & 参考答案)
[Q1: TDengine 和 InfluxDB 怎么选?](#Q1: TDengine 和 InfluxDB 怎么选?)
[Q2: 超级表为什么快?](#Q2: 超级表为什么快?)
[Q3: TDengine 如何处理大量设备同时写入?](#Q3: TDengine 如何处理大量设备同时写入?)
[Q4: 和常规关系型数据库比有什么缺陷?](#Q4: 和常规关系型数据库比有什么缺陷?)
[Q5: 数据过期删除怎么做的?](#Q5: 数据过期删除怎么做的?)
[Q6: TDengine 3.x 和 2.x 的区别?](#Q6: TDengine 3.x 和 2.x 的区别?)
[Q7: 怎么保证数据不丢?](#Q7: 怎么保证数据不丢?)
[六、30 分钟快速上手指南(自检清单)](#六、30 分钟快速上手指南(自检清单))
一、TDengine 是什么?
TDengine 是涛思数据(TAOS Data)打造的高性能、云原生、时序数据库(Time-Series Database, TSDB)。
一句话定位 :专为物联网(IoT)、工业互联网、车联网、运维监控等场景设计的时序数据平台,核心解决海量时间序列数据的高效写入、存储、查询和分析问题。
和普通数据库的区别
| 维度 | 传统关系库(MySQL/PostgreSQL) | 普通时序库(InfluxDB) | TDengine |
|---|---|---|---|
| 数据模型 | 松散的行/列 | 标签+字段 无模式 | 超级表(STable)+ 子表,强模式 |
| 写入性能 | 一般(万级/秒) | 较好(十万级/秒) | 千万级/秒/节点 |
| 压缩比 | 低(一般 2~5x) | 中(5~10x) | 高(10~20x) |
| 查询能力 | 标准 SQL | 类 SQL 有限 | 全功能 SQL,支持窗口、聚合、降采样 |
| 部署运维 | 成熟 | 一般 | 单进程一体,无外部依赖 |
核心特性(面试要点)
-
SQL 兼容 --- 支持标准 SQL(SELECT, INSERT, CREATE, JOIN, 窗口函数等),学习成本极低
-
超级表(STable) --- 核心抽象:一张模板表 + 按标签(tags)自动分表,实现千万级设备统一管理
-
列式存储 + 高效压缩 --- 按列存储,时序特有压缩算法(有损/无损),压缩比 10~20x
-
时间分区 --- 自动按时间分片(VNODES),支持过期自动删除
-
预计算 --- 窗口聚合、降采样、连续查询内建高效
-
云原生架构 --- 支持集群、多副本、弹性伸缩
-
无外部依赖 --- 一个
taosd进程搞定所有,不依赖 ZooKeeper/HDFS -
多种接入 --- JDBC/RESTful/MQTT/Telegraf/Prometheus 等
二、核心组件(架构角度)
2.1 taosd --- 主服务进程
单进程包含全部功能:SQL 解析、存储引擎、时序引擎、集群通信、数据均衡。
启动方式:
systemctl start taosd # Linux docker start tdengine # Docker
2.2 taos --- 原生命令行客户端
连接 taosd 执行 SQL。
taos # 默认连接本地 taos -h 192.168.1.100 -P 6030 # 连接远程
2.3 超级表(STable / Super Table)
这是 TDengine 的灵魂概念,面试必问。
java
CREATE STABLE meters (
ts TIMESTAMP, -- 时间戳(主键)
value FLOAT, -- 采集值
phase INT -- 相别
) TAGS (
location BINARY(64), -- 标签:地理位置
groupId INT -- 标签:分组
);
超级表 = 模板,定义:
-
数据列(Data Columns) --- 随时间变化的值,如 CPU 使用率、温度、电压
-
标签列(Tags) --- 固定不变的元数据,如设备型号、地理位置、所属分组
子表 = 超级表的实例化:
-
一个具体设备对应一张子表
-
标签值在子表创建时固定,后续不变
-
所有子表共享超级表的数据列结构
查询优势:按标签过滤时,TDengine 自动只扫描匹配的子表,性能极佳。
2.4 VNode(虚拟节点)
-
数据分布的最小逻辑单元
-
一个 VNode 包含多个子表的数据
-
多个 VNode 组成 VGroup,VGroup 是数据复制的最小单元
2.5 MNode(管理节点)
-
集群元数据管理(表结构、节点信息、用户权限)
-
支持多副本,使用 RAFT 协议选主维持一致性
2.6 QNode(查询节点)
-
3.x 新增
-
分布式 SQL 查询协调
-
将查询计划拆分到各 VNode 并行执行,再合并结果
2.7 时间分区
-
数据自动按时间分区(默认 1440 分钟/天)
-
分区内数据顺序写入,保证极高的写入性能
-
查询时通过分区裁剪只扫相关分区
三、工作原理(面试怎么答)
面试话术示例(1 分钟版):
"TDengine 之所以快,我认为核心在于三点:
第一,数据模型设计得好 --- 超级表将设备元数据(标签)和数据分离,同一设备的数据物理上连续存储,写入时顺序追加,天然避免了随机 IO。每台设备一张子表,查一台设备就是扫一个文件,不是扫整张表。
第二,列式存储 + 时序压缩 --- 按列存储,时间戳用差值编码(只存 delta),浮点数用施普林格编码或二阶差分,实测压缩比能达到 10~20 倍。存储成本低,查的时候 IO 量也少。
第三,计算下推 --- 窗口聚合、降采样直接在存储层完成,不需要把原始数据拉到应用层算。比如
SELECT AVG(cpu) INTERVAL(5m),数据在扫盘时就聚合完了,查询性能比 MySQL 快几十倍。而且它完全兼容 SQL,团队不需要额外学习新语法。"
四、实战案例:监控 100 台服务器 CPU 使用率
端到端实战,从安装到查询,每一步都写清楚。
4.1 安装 TDengine
方式一:Docker(推荐,5 分钟上手)
java
# 拉取并启动
docker run -d --name tdengine \
-p 6030:6030 -p 6041:6041 -p 6043:6043 -p 6044:6044 \
tdengine/tdengine:3.3.5.4
# 验证
docker exec -it tdengine taos -V
端口说明:
-
6030 → 原生 TCP 协议(JDBC 连接)
-
6041 → RESTful HTTP 接口
-
6043 → 集群内部通信
-
6044 → 集群同步
方式二:直接安装(Linux)
java
# Ubuntu/Debian
wget https://www.tdengine.com/assets-download/TDengine-3.3.5.4-Linux-x64.deb
sudo dpkg -i TDengine-3.3.5.4-Linux-x64.deb
sudo systemctl start taosd
taos -V
4.2 创建数据库和超级表
sql
# 进入 TDengine Shell
taos
-- 创建数据库
-- KEEP 30: 数据保留 30 天自动删除
-- DURATION 10: 每 10 天一个数据文件分区
-- BUFFER 32: 写入缓存 32MB
-- CACHESIZE 4: 元数据缓存 4MB
CREATE DATABASE monitor KEEP 30 DURATION 10 BUFFER 32 CACHESIZE 4;
USE monitor;
-- 创建超级表:服务器 CPU 监控
CREATE STABLE cpu_metrics (
ts TIMESTAMP, -- 采集时间
cpu_usage FLOAT, -- CPU 使用率 (%)
mem_usage FLOAT, -- 内存使用率 (%)
load_1m FLOAT -- 1 分钟负载
) TAGS (
hostname BINARY(32), -- 主机名
region BINARY(16), -- 区域
env BINARY(8) -- 环境 (prod/test/staging)
);
4.3 创建子表(每台服务器一张)
sql
-- 为每台服务器创建子表
CREATE TABLE server_web_01 USING cpu_metrics TAGS('web-01', 'us-east-1', 'prod');
CREATE TABLE server_web_02 USING cpu_metrics TAGS('web-02', 'us-east-1', 'prod');
CREATE TABLE server_db_01 USING cpu_metrics TAGS('db-01', 'us-east-2', 'prod');
CREATE TABLE server_cache_01 USING cpu_metrics TAGS('cache-01', 'ap-southeast-1', 'staging');
CREATE TABLE server_db_02 USING cpu_metrics TAGS('db-02', 'eu-west-1', 'prod');
-- 查一下有多少个子表
SELECT COUNT(*) FROM information_schema.ins_tables WHERE db_name = 'monitor';
4.4 写入数据
方式一:单条写入
sql
INSERT INTO server_web_01 (ts, cpu_usage, mem_usage, load_1m)
VALUES (NOW, 45.2, 62.1, 2.3);
方式二:单行写入多张表(事务性)
sql
INSERT INTO
server_web_01 (ts, cpu_usage, mem_usage, load_1m) VALUES (NOW, 47.8, 63.0, 2.5)
server_db_01 (ts, cpu_usage, mem_usage, load_1m) VALUES (NOW, 32.1, 78.4, 1.8)
server_cache_01 (ts, cpu_usage, mem_usage, load_1m) VALUES (NOW, 12.3, 45.6, 0.9);
方式三:批量造数据(用于测试,造 1000 条)
sql
-- 生成从 NOW - 1000s 到 NOW 每秒一条的伪随机数据
INSERT INTO server_web_01
SELECT
_wstart AS ts,
RANDOM() % 100 AS cpu_usage,
RANDOM() % 100 AS mem_usage,
(RANDOM() % 100) / 10.0 AS load_1m
FROM
(SELECT _wstart FROM FILL(ts, 1s) RANGE(NOW - 1000s, NOW));
-- 验证写入成功
SELECT COUNT(*) FROM server_web_01;
4.5 查询数据(各种场景)
场景 1:查一台服务器最近 1 小时的数据
sql
SELECT * FROM server_web_01
WHERE ts >= NOW - 1h
ORDER BY ts DESC;
场景 2:查所有服务器的平均 CPU(过去 1 小时)
sql
SELECT
AVG(cpu_usage) AS avg_cpu,
MAX(cpu_usage) AS max_cpu,
MIN(cpu_usage) AS min_cpu,
COUNT(*) AS data_points
FROM cpu_metrics
WHERE ts >= NOW - 1h;
场景 3:按区域聚合
sql
SELECT
region,
AVG(cpu_usage) AS avg_cpu,
AVG(mem_usage) AS avg_mem
FROM cpu_metrics
WHERE ts >= NOW - 1d
PARTITION BY region;
场景 4:降采样 --- 每 5 分钟一个聚合点
sql
SELECT
_wstart AS window_start,
AVG(cpu_usage) AS avg_cpu,
MAX(cpu_usage) AS peak_cpu,
MIN(cpu_usage) AS min_cpu
FROM cpu_metrics
WHERE ts >= NOW - 1h
INTERVAL(5m);
场景 5:找出 CPU > 90% 的异常时刻
sql
SELECT
tbname, hostname, ts, cpu_usage, mem_usage
FROM cpu_metrics
WHERE cpu_usage > 90 AND ts >= NOW - 1d
ORDER BY ts DESC;
场景 6:TOP N 分析 --- CPU 最高的前 10 台服务器
sql
SELECT
tbname, hostname,
AVG(cpu_usage) AS avg_cpu
FROM cpu_metrics
WHERE ts >= NOW - 1h
PARTITION BY tbname
ORDER BY avg_cpu DESC
LIMIT 10;
场景 7:跨表 JOIN 查询(高级)
sql
-- 查 web-01 和 db-01 在相同时刻的 CPU 对比
SELECT
a.ts,
a.cpu_usage AS web_cpu,
b.cpu_usage AS db_cpu,
(a.cpu_usage - b.cpu_usage) AS diff
FROM server_web_01 a
JOIN server_db_01 b
ON a.ts = b.ts;
4.6 Java(Spring Boot)完整集成
pom.xml 添加依赖:
XML
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
application.yml 配置:
XML
spring:
datasource:
url: jdbc:TAOS-RS://localhost:6041/monitor
username: root
password: taosdata
driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
实体类:
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CpuMetric {
private Date ts;
private Double cpuUsage;
private Double memUsage;
private Double load1m;
private String hostname;
private String region;
private String env;
}
DAO 层:
java
@Repository
public class CpuMetricDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/** 单条写入(自动根据 hostname 选子表) */
public void insert(CpuMetric metric) {
String tableName = "server_" + metric.getHostname().replace("-", "_");
String sql = String.format(
"INSERT INTO %s (ts, cpu_usage, mem_usage, load_1m) VALUES (?, ?, ?, ?)",
tableName
);
jdbcTemplate.update(sql,
metric.getTs(), metric.getCpuUsage(), metric.getMemUsage(), metric.getLoad1m()
);
}
/** 批量写入(按 hostname 分组,每组一个 batch --- 性能最好) */
public void batchInsert(List<CpuMetric> metrics) {
// 按 hostname 分组
Map<String, List<CpuMetric>> grouped = metrics.stream()
.collect(Collectors.groupingBy(m -> "server_" + m.getHostname().replace("-", "_")));
grouped.forEach((tableName, list) -> {
String sql = String.format(
"INSERT INTO %s (ts, cpu_usage, mem_usage, load_1m) VALUES (?, ?, ?, ?)",
tableName
);
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
CpuMetric m = list.get(i);
ps.setTimestamp(1, new Timestamp(m.getTs().getTime()));
ps.setDouble(2, m.getCpuUsage());
ps.setDouble(3, m.getMemUsage());
ps.setDouble(4, m.getLoad1m());
}
@Override
public int getBatchSize() {
return list.size();
}
});
});
}
/** 查最近 N 条记录 */
public List<CpuMetric> queryRecent(String hostname, int limit) {
String tableName = "server_" + hostname.replace("-", "_");
String sql = String.format(
"SELECT ts, cpu_usage, mem_usage, load_1m FROM %s ORDER BY ts DESC LIMIT ?",
tableName
);
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(CpuMetric.class), limit);
}
/** 按区域聚合查询 */
public List<Map<String, Object>> aggregateByRegion(int hoursAgo) {
String sql = """
SELECT region,
AVG(cpu_usage) AS avg_cpu,
AVG(mem_usage) AS avg_mem,
COUNT(*) AS total_points
FROM cpu_metrics
WHERE ts >= NOW - ?h
PARTITION BY region
""";
return jdbcTemplate.queryForList(sql, hoursAgo);
}
}
4.7 对接 Grafana 可视化
- 在 Grafana 中安装 TDengine 数据源插件:
grafana-cli plugins install tdengine-datasource
-
添加数据源:
-
URL:
http://你的服务器:6041 -
数据库:
monitor -
用户名/密码:
root/taosdata
-
-
一个示例面板的查询 SQL:
sql
SELECT _wstart AS t, AVG(cpu_usage) AS avg_cpu
FROM cpu_metrics
WHERE ts >= NOW - 1h
AND region = 'us-east-1'
INTERVAL(30s)
4.8 对接 Prometheus(面试加分项)
TDengine 支持 Prometheus remote write/read 协议。
Prometheus 配置:
# prometheus.yml
remote_write:
- url: "http://你的服务器:6041/prometheus/v1/write?db=monitor"
remote_read:
- url: "http://你的服务器:6041/prometheus/v1/read?db=monitor"
原理:Prometheus 采集的指标数据直接写入 TDengine,Grafana 再从 TDengine 查询,实现采集和存储解耦,解决 Prometheus 单机存储瓶颈。
五、面试高频题 & 参考答案
Q1: TDengine 和 InfluxDB 怎么选?
答: 选型不只看性能。
-
选 TDengine:团队有 SQL 经验、数据有固定标签结构(1 万+ 设备有相同采集维度),需要高吞吐写入。超级表模型更匹配,SQL 兼容降低学习成本。
-
选 InfluxDB:多变的无模式数据、临时探索分析为主、生态成熟度要求高。
-
性能差距:TDengine 写入吞吐(千万级/秒)通常比 InfluxDB 高一个数量级,压缩比也更好。
Q2: 超级表为什么快?
答: 三个核心原因:
-
写即顺序 --- 一张子表只存一个设备的数据,写入是顺序追加,天然无随机 IO
-
标签→分区裁剪 --- 标签和数据分离,按标签过滤时只扫描匹配的子表,不需要全表扫
-
时间分区 --- 数据按时间分区存储,查询时剪切无关分区
Q3: TDengine 如何处理大量设备同时写入?
答: 三层缓冲机制:
-
数据先进入内存的 WriteCache
-
达到一定量或超时后合并写入列式文件
-
VNode 分担写入压力 单节点实测 800 万+ 数据点/秒。
Q4: 和常规关系型数据库比有什么缺陷?
答: 主要三点:
-
事务能力弱 --- 不支持跨表事务,ACID 不如 MySQL
-
小数据量无优势 --- 100 万行以内 MySQL 够用,TDengine 优势体现不出来
-
生态仍在成长 --- 不如 InfluxDB/Prometheus 成熟,某些工具对接需要自己写适配
Q5: 数据过期删除怎么做的?
答: 建库时指定 KEEP 参数(如 KEEP 30 天),TDengine 自动在后台清理过期数据文件。不需要写 DELETE 语句,避免了传统数据库大数据量删除的性能灾难。
Q6: TDengine 3.x 和 2.x 的区别?
答: 3.x 是架构重写,主要变化:
-
从 2.x 的"一个节点一个 dnode"变为"计算存储分离"架构
-
新增 QNode(查询节点)解耦查询负载
-
支持 Kubernetes 部署(Operator)
-
更好的 SQL 兼容性(窗口函数等)
Q7: 怎么保证数据不丢?
答: 三种机制配合:
-
多副本 --- 每个 VGroup 配置 1~3 副本,写入同步复制
-
WAL --- 先写 WAL 日志再写缓存,宕机可恢复
-
RAFT --- 管理节点用 RAFT 保证元数据一致性
六、30 分钟快速上手指南(自检清单)
| 步骤 | 操作 | 预期结果 |
|---|---|---|
| 1. 安装 | docker run tdengine/tdengine |
taosd 启动 |
| 2. 连接 | taos 进入 shell |
看到 taos> 提示符 |
| 3. 建库 | CREATE DATABASE demo; USE demo; |
切换数据库成功 |
| 4. 建超级表 | CREATE STABLE ... |
超级表创建成功 |
| 5. 建子表 | CREATE TABLE ... USING ... TAGS(...) |
子表创建成功 |
| 6. 写入 | INSERT INTO ... VALUES(...) |
返回 >0 受影响行数 |
| 7. 查询 | SELECT * FROM cpu_metrics |
返回数据 |
| 8. 降采样 | SELECT ... INTERVAL(1m) |
分钟级聚合结果 |
| 9. 标签过滤 | WHERE region='us-east-1' |
只返回该区域数据 |
| 10. Java 集成 | 引入 taos-jdbcdriver | CRUD 正常 |
七、学习路线推荐
Day 1: 安装 + 建库建表 + 写入查询(今天的内容)
Day 2: 超级表 + 子表机制深入 + 批量写入性能测试
Day 3: 降采样聚合 + 连续查询 + 数据保留策略
Day 4: Java/Spring Boot 接入 + 批量写入优化
Day 5: Grafana 可视化 + Prometheus 对接
Day 6: 集群搭建 + 多副本 + 扩容缩容
Day 7: 性能调优 + 面试题复盘