适用版本:TDengine v3.x(v3.3.x / v3.4.x) | 最后更新:2026-05-12
概述
TDengine 是一款专为时序数据场景设计的高性能分布式数据库。它不是在已有数据库上做的优化包装,而是从零开始自主研发的全栈时序数据处理平台,内置了数据库、缓存、流计算和数据订阅四大核心功能,一套系统即可替代传统方案中 Kafka + Redis + HBase + Flink 的复杂组合。
TDengine 的架构设计基于两个基本假设:
- 任何单台硬件/软件都不可靠——所以从第一天起就按分布式高可靠架构设计
- 任何单台计算机都无法处理海量数据——所以支持水平扩展,通过节点虚拟化和负载均衡高效利用异构集群资源
本文从进程层、逻辑层、通信层三个维度全面剖析 TDengine 的整体架构。
核心概念速查表
| 概念 | 说明 |
|---|---|
| dnode | 数据节点,taosd 进程在一台物理机上的一个运行实例,是部署和运维的最小单元 |
| vnode | 虚拟节点,dnode 内部的逻辑存储单元,负责一部分表的时序数据存储和查询 |
| vgroup | 虚拟节点组,由分布在不同 dnode 上的多个 vnode 组成,通过 Raft 协议保证高可用 |
| mnode | 管理节点,负责集群元数据管理(用户/数据库/超级表/vgroup 分配等),最多 3 个 |
| qnode | 计算节点,专门执行查询计算任务,实现存储与计算分离 |
| snode | 流计算节点,专门处理流计算任务,实现流计算与批计算分离 |
| bnode | 备份节点(仅企业版),用于 S3 或外部存储的数据备份 |
| taosc | 客户端驱动,应用程序通过它与集群交互,负责路由、缓存和最后一级聚合 |
| taosAdapter | RESTful/WebSocket 网关,为不使用原生驱动的场景提供 HTTP 接口 |
详细架构解析
1. 进程架构:一个 taosd 进程,多种逻辑节点
TDengine 服务端只有一个进程——taosd。它不是单一功能的进程,而是一个节点容器,在一个进程内同时运行多种逻辑节点:
┌─────────────────────────────── taosd 进程 ──────────────────────────────┐
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ dnode │ │ mnode │ │ vnode │ │ qnode │ │ snode │ ... │
│ │ 管理模块 │ │ (0或1个) │ │(0~N个) │ │(0或1个)│ │(0或1个)│ │
│ └──────────┘ └──────────┘ └────────┘ └────────┘ └────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ RPC / Transport 层 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ TFS (分级文件系统) │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
关键设计:每个 dnode 上,mnode/qnode/snode 最多各一个;vnode 可以有多个(取决于硬件资源和数据库分片数)。哪些节点在哪些 dnode 上运行,由 mnode 统一管理和调度。
1.1 taosd 启动流程
taosd 启动时按以下顺序初始化:
- 解析命令行参数——读取配置文件路径、数据目录等
- 初始化基础设施——日志系统、信号处理、分级文件系统(TFS)
- 创建 dnode 管理对象——为所有支持的节点类型(dnode/mnode/vnode/qnode/snode/bnode/xnode)注册生命周期管理函数
- 判断本节点需要运行哪些逻辑节点——根据配置和集群状态决定
- 逐个打开各节点——每种节点执行自己的初始化逻辑(例如 vnode 会从磁盘恢复所有 vgroup)
- 逐个启动各节点——打开 RPC 端口,开始接受请求
- 主循环等待退出信号——收到 SIGTERM/SIGINT 后按反向顺序关闭所有节点
taosd 内部维护一个 7 槽位的节点管理数组,每个槽位对应一种节点类型(dnode 自身、mnode、vnode、qnode、snode、bnode、xnode),为每种节点类型提供统一的 open/close/start/stop 生命周期接口。
2. VNode:数据存储的核心单元
VNode 是 TDengine 中最重要的逻辑概念——它是数据存储、查询和复制的基本单元。
2.1 VNode 内部模块
每个 VNode 内部包含 5 个核心子模块,各司其职:
┌─────────────────── VNode (vgId=2) ────────────────────┐
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
│ │ META │ │ TSDB │ │ WAL │ │ TQ │ │
│ │ 元数据 │ │ 时序数据 │ │ 预写日志 │ │ 消息队列 │ │
│ │ (B+Tree) │ │ (LSM) │ │ │ │ (订阅) │ │
│ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ SMA │ │ Buffer Pool │ │
│ │ 预聚合 │ │ (3段内存池) │ │
│ └──────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Raft Sync 模块 │ │
│ │ (Leader 选举 / 日志复制 / 快照传输) │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
| 子模块 | 存储结构 | 职责 |
|---|---|---|
| META | B+Tree(自研 TDB 引擎) | 存储表的元数据:超级表 Schema、子表 Tag 值、表 UID 映射、Tag 索引 |
| TSDB | LSM-Tree(MemTable → SST 文件) | 存储时序数据,按列式存储和压缩,支持多级文件组织 |
| WAL | 顺序追加日志文件 | 写前日志,保证崩溃恢复;同时也是 Raft 日志和 TMQ 消费的数据源 |
| TQ | 基于 WAL/快照 | 管理数据订阅(TMQ)的消费进度和流计算任务的调度 |
| SMA | 独立 SMA 文件 | 预聚合引擎,支持 RSMA(Rollup SMA)多级降采样和 TSMA(用户自定义预聚合) |
2.2 VNode 的数据分片机制
当创建一个数据库时,mnode 会根据 VGROUPS 参数创建指定数量的 vgroup。系统将整个 32 位 hash 空间 [0, 0xFFFFFFFF] 均匀划分给各个 vgroup,每个 vgroup 负责一段 hash 范围:
数据库 "power" (VGROUPS 4):
vgroup 1: hashRange [0x00000000, 0x3FFFFFFF] → dnode1
vgroup 2: hashRange [0x40000000, 0x7FFFFFFF] → dnode2
vgroup 3: hashRange [0x80000000, 0xBFFFFFFF] → dnode3
vgroup 4: hashRange [0xC0000000, 0xFFFFFFFF] → dnode1
当写入数据时,客户端对表名计算一致性 hash 值,然后确定该表属于哪个 vgroup,再将请求发送到对应的 vnode。也就是说,同一张表的所有数据始终在同一个 vnode 中。
2.3 VNode 的 Buffer Pool(三段缓冲池)
VNode 使用一个 3 段循环缓冲池来管理内存写入:
┌───────────┐ ┌───────────┐ ┌───────────┐
│ BufPool-0 │───→│ BufPool-1 │───→│ BufPool-2 │──→ (循环)
│ (inUse) │ │(onCommit) │ │ (free) │
└───────────┘ └───────────┘ └───────────┘
↑ │
新数据写入 后台线程刷盘
- inUse:当前接收新写入的缓冲池
- onCommit:正在被后台线程刷写到磁盘的缓冲池
- free:已完成刷盘、等待被重新使用的缓冲池
当 inUse 的内存使用达到阈值时,触发切换:inUse → onCommit,free → inUse,然后后台 Commit 线程将 onCommit 中的数据持久化到 META(B+Tree)和 TSDB(SST 文件)。3 段设计实现了写入和落盘的流水线,确保在刷盘期间仍有空闲段接收新写入,提高写入吞吐。
2.4 VNode 的写入路径
一条数据从客户端到磁盘的完整路径:
客户端 INSERT SQL
↓
taosc 解析 SQL,计算表名 hash,定位 vgroup
↓
RPC 发送写入请求到 leader vnode
↓
leader vnode 发起 Raft Propose
↓ [Raft 日志复制到 follower]
多数派确认后 Apply
↓
┌──────────────────────────────────┐
│ 1. 写入 WAL(保证持久性) │
│ 2. 写入 MemTable(内存中的数据) │
│ - META 模块:更新表元数据 │
│ - TSDB 模块:插入时序数据 │
│ 3. 触发 SMA(如果配置了 RSMA) │
└──────────────────────────────────┘
↓ [当 MemTable 达到阈值]
后台 Commit 线程:MemTable → 磁盘文件
↓
标记对应 WAL 已落盘,可回收
3. MNode:集群的大脑
MNode 是集群的元数据管理中心,负责管理所有的全局信息。
3.1 MNode 管理的对象
MNode 使用自研的 SDB(State Database) 引擎存储元数据,SDB 本身也通过 Raft 协议在多个 mnode 之间复制,保证高可用。SDB 管理的主要对象包括:
| 类别 | 管理对象 | 说明 |
|---|---|---|
| 集群管理 | Cluster、DNode、MNode、QNode、SNode | 集群拓扑、节点注册和状态 |
| 数据管理 | Database、VGroup、Super Table、SMA | 数据库配置、分片分布、超级表 Schema |
| 用户安全 | User、Role、Auth | 用户认证、RBAC 权限控制 |
| 流计算 | Stream、StreamTask | 流计算任务定义和调度 |
| 数据订阅 | Topic、Consumer、Subscribe、Offset | TMQ 主题和消费者管理 |
| 扩展功能 | Func(UDF)、Index、View、Compact | 自定义函数、索引、视图、压缩任务 |
| 事务 | Trans | 分布式事务状态机 |
3.2 MNode 的定时任务
MNode 内部运行一个定时器线程,周期性执行以下关键任务:
| 任务 | 说明 |
|---|---|
| 推进未完成的事务 | 重试因超时或节点宕机而暂停的分布式事务 |
| TMQ 消费者负载均衡 | 在消费者加入/离开时重新分配分区 |
| TTL 表清理 | 检查并删除已过期的表 |
| DNode 离线检测 | 发现心跳超时的 dnode,标记其上的 vgroup 为离线 |
| Compaction 进度推进 | 查询各 vnode 的压缩进度,更新状态机 |
| WAL/数据修剪 | 清理已不需要的 WAL 和过期数据 |
3.3 MNode 的分布式事务
MNode 通过事务对象来实现跨节点的 DDL 操作原子性。例如 CREATE DATABASE 需要在多个 dnode 上创建 vnode,这个过程通过一个多阶段事务来保证:
CREATE DATABASE 事务流程:
1. MNode 创建事务对象,记录所有操作步骤
2. 通过 Raft 将事务复制到所有 mnode 副本
3. Leader mnode 按步骤执行:
- 向 dnode1 发送 "创建 vnode1" 请求
- 向 dnode2 发送 "创建 vnode2" 请求
- 收集所有响应
4. 全部成功 → 事务标记为 committed
任一失败 → 执行回滚操作
5. 定时器会自动重试未完成的事务
事务设计采用可重试策略和幂等保证——如果某个 dnode 已经完成了操作,重试时直接返回成功而不报错。
4. QNode:存算分离的关键
QNode 是独立于 VNode 的纯计算节点。当集群配置了 QNode 后,查询引擎会将计算密集型的操作(如聚合、排序、JOIN)调度到 QNode 执行,VNode 只负责数据扫描和过滤。
无 QNode 的查询:
taosc → VNode(扫描 + 计算 + 返回结果)
有 QNode 的查询:
taosc → QNode(接收计算任务)
↓
QNode 向多个 VNode 请求数据
↓
QNode 执行聚合/排序/JOIN
↓
QNode 返回结果给 taosc
如果集群中没有可用的 QNode,所有计算任务将回退到 VNode 中执行。
5. SNode:流计算的专用节点
SNode 专门处理流计算(CREATE STREAM)任务。当集群配置了 SNode 后,MNode 会将流计算任务调度到 SNode 执行,从而避免流计算占用 VNode 的写入和查询资源。
流计算任务分配:
有 SNode → MNode 调度流任务到 SNode
无 SNode → 流任务在 VNode 中执行(与数据存储共享资源)
6. Taosc:智能客户端驱动
Taosc 不是一个简单的连接库——它是一个智能路由和缓存引擎,封装了所有分布式系统的复杂性。
6.1 Taosc 的核心能力
| 能力 | 说明 |
|---|---|
| 元数据缓存 | 缓存 vgroup 分布信息和表 schema,避免每次操作都查询 mnode |
| 路由和重定向 | 自动发现 mnode、自动追踪 vgroup leader 变化 |
| SQL 解析和规划 | 在客户端侧完成 SQL 解析、语义分析和物理计划拆分 |
| 任务调度 | 将分布式子任务并行分发到各 vnode/qnode |
| 结果合并 | 收集各节点返回的局部结果,执行最后一级聚合 |
| 心跳管理 | 定期向 mnode 发送心跳,维护连接和刷新元数据 |
6.2 连接建立流程
应用程序调用 taos_connect(host, user, pass, db, port)
↓
taosc 向配置的 firstEp 发送连接请求
↓
如果该 dnode 不是 mnode → 返回 mnode 的地址列表 → 自动重定向
↓
mnode 返回连接响应:
- 集群 ID
- 服务器时间戳
- mnode 地址列表
↓
taosc 缓存集群信息,启动心跳线程(默认每 1.5 秒)
↓
返回连接句柄给应用
6.3 查询执行全流程
应用调用 taos_query("SELECT avg(voltage) FROM meters WHERE location='Beijing'")
↓
1. taosc 解析 SQL,构建抽象语法树(AST)
↓
2. 元数据查找(带缓存):
- 检查缓存中是否有 "meters" 表的元数据
- 没有 → 向 mnode 请求 vgroup 分布信息
- 没有 → 向 vnode 请求表 schema
↓
3. 语义分析 + 类型检查 + 权限校验
↓
4. 逻辑计划 → 物理计划 → 分布式计划拆分
(将查询拆分为多个子任务,每个子任务对应一个 vnode/qnode)
↓
5. 调度器并行分发子任务到各 vnode/qnode
各节点并行执行扫描和局部聚合
↓
6. 结果合并:
收集各节点的局部结果,taosc 执行最后一级合并/聚合
↓
7. 返回最终结果给应用
7. RPC 通信层
7.1 通信协议
TDengine 集群内所有通信均使用 TCP 协议,通信层基于自研的 RPC 框架。
每个 dnode 维护 4 个独立的 RPC 通道:
| 通道 | 用途 | 说明 |
|---|---|---|
| Server RPC | 监听外部请求 | 接收来自 taosc 和其他 dnode 的请求 |
| Client RPC | 发起通用请求 | 向其他 dnode 发送请求 |
| Status RPC | 状态心跳 | dnode 定期向 mnode 上报状态 |
| Sync RPC | Raft 同步 | 专用于 Raft 日志复制和快照传输 |
7.2 消息路由机制
当 taosd 收到一条 RPC 消息时:
- 版本兼容性检查——确认客户端和服务端版本兼容
- IP 白名单检查(如果启用)——拒绝未授权的来源
- 根据消息类型查路由表——确定由哪种节点类型处理(mnode/vnode/qnode 等)
- 获取对应的节点——如果需要 mnode 但本 dnode 没部署 mnode → 返回重定向信息
- 放入对应的工作队列——写入请求进写入队列,查询请求进查询队列
- 工作线程从队列取出消息并执行
7.3 工作队列隔离
TDengine 使用多种工作队列来隔离不同类型的操作,确保写入不阻塞查询、查询不阻塞 Raft 同步:
| 队列类型 | 用途 |
|---|---|
| Write Queue | 写入操作(Raft Propose) |
| Apply Queue | Raft Apply(已提交的写入执行) |
| Query Queue | 查询执行 |
| Fetch Queue | 查询结果获取 |
| Sync Queue | Raft 同步消息处理 |
| Stream Queue | 流计算任务 |
8. VGroup 与 Raft 共识
8.1 VGroup 的作用
VGroup(虚拟节点组)是 TDengine 实现高可用的关键机制。一个 VGroup 由分布在不同 dnode 上的多个 VNode 组成,它们之间通过 Raft 一致性协议 保持数据同步。
vgroup 3 (REPLICA 3):
┌──────────┐ ┌──────────┐ ┌──────────┐
│ VNode │ │ VNode │ │ VNode │
│ (Leader) │←──→│(Follower)│←──→│(Follower)│
│ dnode-1 │ │ dnode-2 │ │ dnode-3 │
└──────────┘ └──────────┘ └──────────┘
↑
写入请求
- 写操作 只能在 Leader VNode 上执行
- Leader 将 WAL 日志异步复制到 Follower
- 多数派确认后,数据才被认为已提交
- Leader 宕机时,剩余 Follower 自动选举新 Leader
8.2 副本数与 VGroup 数
-- 创建 3 副本、8 个 vgroup 的数据库
CREATE DATABASE power REPLICA 3 VGROUPS 8;
-- 这会创建 8 × 3 = 24 个 VNode,分布在至少 3 个 dnode 上
- 副本数(REPLICA):创建数据库时指定,默认 1,最大 3。决定每个 VGroup 中的 VNode 数量。
- VGroup 数(VGROUPS):创建数据库时指定,默认自动计算。决定数据被分成多少个分片。
- 约束:创建 N 副本的数据库,集群至少需要 N 个 dnode。
9. 集群拓扑示例
一个典型的 3 节点生产集群:
┌─────────── dnode-1 (192.168.1.101:6030) ──────────────┐
│ mnode (Leader) │ vnode(vg=1,Leader) │
│ qnode │ vnode(vg=4,Follower) │
│ snode │ vnode(vg=7,Leader) │
└───────────────────────────────────────────────────────┘
│ TCP
┌─────────── dnode-2 (192.168.1.102:6030) ──────────────┐
│ mnode (Follower) │ vnode(vg=1,Follower) │
│ qnode │ vnode(vg=5,Leader) │
│ │ vnode(vg=8,Follower) │
└───────────────────────────────────────────────────────┘
│ TCP
┌─────────── dnode-3 (192.168.1.103:6030) ──────────────┐
│ mnode (Follower) │ vnode(vg=1,Follower) │
│ qnode │ vnode(vg=6,Leader) │
│ │ vnode(vg=9,Leader) │
└───────────────────────────────────────────────────────┘
10. 端到端完整操作流程
10.1 写入流程(INSERT)
App:INSERT INTO d1001 USING meters TAGS('Beijing',2) VALUES(now, 10.3, 219, 0.31)
│
↓
Taosc:
├── 解析 SQL
├── 查缓存:"meters" 的 vgroup 分布信息?
│ → 没有 → 向 mnode 请求 → mnode 返回各 vgroup 的 hash 范围和 leader 地址
├── 计算 "d1001" 的 hash → 命中 vgroup 3
├── 查缓存:d1001 的 schema?
│ → 没有 → 向 vgroup 3 的 leader 请求 → 返回 schema
├── 如果 d1001 不存在 → 自动建表(因为用了 USING 子句)
├── 编码数据,发送写入请求到 vgroup 3 的 leader
│
↓
VNode (Leader, vgroup 3):
├── Raft Propose → 复制 WAL 到 Follower
├── 多数派确认 → Apply
├── 写 WAL → 写 MemTable
└── 返回成功
│
↓
Taosc:缓存 leader 信息,返回成功给 App
10.2 查询流程(SELECT)
App:SELECT avg(voltage) FROM meters WHERE location='Beijing' AND ts > now-1h
│
↓
Taosc:
├── 解析 SQL → 语义分析 → 逻辑计划 → 物理计划
├── 拆分为分布式子计划:
│ ├── 子任务 1 → vgroup 1:扫描 + 过滤 + 局部聚合
│ ├── 子任务 2 → vgroup 2:扫描 + 过滤 + 局部聚合
│ ├── 子任务 3 → vgroup 3:扫描 + 过滤 + 局部聚合
│ └── 合并任务 → qnode 或 taosc 自身:合并局部结果
├── 并行分发子任务到各 vnode/qnode
├── 各节点执行:
│ ├── Tag 过滤:location='Beijing' → 筛选出符合条件的子表
│ ├── 时间过滤:ts > now-1h → 定位文件组和数据块
│ ├── 扫描数据 → 局部 avg 计算
│ └── 返回局部结果
├── taosc/qnode 合并所有局部结果 → 最终 avg
└── 返回给 App
11. 存储层概览
11.1 数据文件组织
每个 VNode 的数据目录结构如下:
/var/lib/taos/vnode/vnode2/
├── vnode.json # VNode 配置和状态信息
├── wal/ # WAL 预写日志
│ ├── 00000000.log
│ └── 00000001.log
├── meta/ # 元数据 B+Tree
│ ├── main.tdb
│ └── main.tdb-journal
└── tsdb/ # 时序数据(LSM 结构)
├── v2f1000.head # BRIN 索引(块范围索引)
├── v2f1000.data # 列式数据块
├── v2f1000.sma # 预计算统计信息(min/max/sum)
├── v2f1000.stt # 碎片数据(STT 文件)
└── v2f1000.tomb # 删除记录
11.2 三层数据存储
| 层级 | 存储位置 | 数据内容 |
|---|---|---|
| 数据库元数据 | MNode(SDB) | 集群拓扑、用户、数据库配置、超级表 Schema、VGroup 分布 |
| 表元数据 | VNode(META) | 子表名 → UID 映射、Tag 值、表 Schema、Tag 索引 |
| 时序数据 | VNode(TSDB) | 按 (时间戳, 版本号) 排序的列式时序数据 |
12. 关键配置参数
| 参数 | 默认值 | 说明 |
|---|---|---|
fqdn | hostname | dnode 的 FQDN,用于集群通信 |
serverPort | 6030 | taosd 监听端口 |
firstEp | - | 集群第一个 dnode 的 endpoint,新节点加入时指定 |
secondEp | - | 备用 endpoint,firstEp 不可达时使用 |
dataDir | /var/lib/taos | 数据文件存储目录,支持多磁盘多级存储 |
tempDir | /tmp | 临时文件目录 |
supportVnodes | CPU×2 | 单个 dnode 支持的最大 VNode 数 |
statusInterval | 1 秒 | dnode 向 mnode 上报状态的间隔 |
13. 线程模型概览
TDengine 的线程模型是多线程、多队列的架构:
taosd 进程中的主要线程组:
┌─ RPC 线程组 ──────────────────────────────────┐
│ Server IO 线程 × N (接收 RPC 请求) │
│ Client IO 线程 × N (发送 RPC 请求) │
└───────────────────────────────────────────────┘
┌─ VNode 线程组(所有 VNode 共享) ─────────────┐
│ Query Worker 线程池 (查询执行) │
│ Fetch Worker 线程池 (结果获取) │
│ Write Worker 线程池 (写入 Raft Propose) │
│ Apply Worker 线程池 (Raft Apply) │
│ Sync Worker 线程池 (Raft 同步) │
│ Stream Worker 线程池 (流计算) │
│ Commit 线程 × 1 (MemTable 刷盘) │
│ Merge/Compact 线程 × 1 (SST 文件合并) │
└───────────────────────────────────────────────┘
┌─ MNode 线程组 ────────────────────────────────┐
│ Read Worker 线程池 (元数据查询) │
│ Write Worker 线程池 (DDL 操作) │
│ Sync Worker 线程池 (Raft 同步) │
│ Timer 线程 × 1 (定时任务) │
└───────────────────────────────────────────────┘
每个 VNode 拥有独立的写入队列、同步队列和 Apply 队列,但共享全局的查询线程池和 Fetch 线程池。这种设计既保证了 VNode 之间写入隔离,又避免了线程资源浪费。
性能考量
- MNode 不是瓶颈:taosc 会缓存 vgroup 分布和表 schema,只有首次访问时才需要联系 mnode。后续操作直接路由到 vnode。
- 写入优化:WAL + MemTable 的追加写模式,配合列式存储和压缩,写入吞吐极高。
- 查询优化:BRIN 索引 + SMA 预计算 + Tag 索引 + 时间分区,多级过滤减少数据扫描量。
- 存算分离:通过 QNode 实现计算与存储的解耦,避免大查询影响写入性能。
- 多级存储:支持 SSD/HDD/S3 多级存储,热数据放 SSD,冷数据放 HDD 或 S3,降低成本。
FAQ
Q1: TDengine 的 vnode 和传统数据库的分片(shard)有什么区别?
VNode 本质上就是一个数据分片,但它更加自包含。每个 VNode 不仅存储时序数据(TSDB),还存储该分片内所有表的元数据(META)、预写日志(WAL)、预聚合数据(SMA)和消费进度(TQ)。这意味着一个 VNode 可以独立完成数据的写入、查询和恢复,不依赖外部的元数据服务。
Q2: MNode 最多只有 3 个,会不会成为单点瓶颈?
不会。MNode 管理的是"低频"的元数据操作(如建库、建表、DDL),这些操作占比很小。真正的高频操作(数据写入和查询)直接路由到 VNode,不经过 MNode。taosc 会缓存所有需要的路由信息,正常情况下只在首次访问时联系 MNode。
Q3: 如果 taosc 连接的 dnode 宕机了怎么办?
taosc 会自动重试连接集群中的其他 dnode。由于每个 dnode 都知道所有 mnode 的 endpoint,taosc 可以通过任意存活的 dnode 重新获取集群信息。对于多副本数据库,如果当前 leader vnode 所在的 dnode 宕机,taosc 会自动连接同一 vgroup 中新选举出的 leader。
Q4: QNode 和 VNode 中执行查询有什么区别?
在 VNode 中执行查询时,扫描和计算共享 VNode 的资源(内存、CPU),大查询可能影响写入性能。使用 QNode 后,VNode 只负责数据扫描和初步过滤,复杂的聚合、排序、JOIN 在 QNode 上执行,实现了存储和计算的资源隔离。
Q5: TDengine 的 Raft 实现有什么特点?
TDengine 自研了 Raft 共识协议实现,支持:
- Leader 选举与自动故障转移
- 日志复制(基于 WAL)
- 快照传输(用于新节点追赶数据)
- 仲裁机制(2 副本场景下的脑裂处理)
MNode 和 VGroup 都使用同一套 Raft 实现,但独立运行各自的 Raft 组。
Q6: 一个 dnode 上可以同时运行多少种节点?
理论上一个 dnode 可以同时运行 mnode + 多个 vnode + qnode + snode + bnode。在小规模部署中,单个 dnode 可以承担所有角色。在大规模生产环境中,建议将 mnode 和 qnode 部署在独立的 dnode 上,避免资源争抢。
Q7: 为什么 TDengine 使用 FQDN 而不是 IP 地址进行通信?
FQDN 比 IP 地址更稳定。在容器化、云部署场景中,IP 地址可能频繁变化,而 FQDN 可以通过 DNS 动态解析。使用 FQDN 使集群能够适应 IP 变化而无需重新配置。
Q8: taosc 缓存了元数据,如果表 schema 变了怎么办?
taosc 的心跳机制会定期向 mnode 同步最新信息。当 schema 发生变更时,mnode 会递增版本号。taosc 在心跳中发现版本号变化后会刷新缓存。如果在刷新之前使用了旧 schema 写入,vnode 会返回 schema 不匹配错误,taosc 会自动重新获取最新 schema 并重试。
参考
- TDengine 官方文档 - 整体架构:https://docs.taosdata.com/tdinternal/arch/
- TDengine 官方文档 - 存储引擎:https://docs.taosdata.com/tdinternal/storage/
- TDengine GitHub 源码:https://github.com/taosdata/TDengine


3987

被折叠的 条评论
为什么被折叠?



