MNode 内部机制深度解析 — SDB、事务引擎与 DDL 处理全链路

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

概述

MNode(Management Node)是 TDengine 集群的"大脑",负责管理所有元数据和协调集群操作。它是一个运行在某些 dnode 上的逻辑节点,最多可以有 3 个副本通过 Raft 协议保持一致性。

本文深入解析 MNode 的四大核心机制:

  1. SDB(System Database):内存中的元数据存储引擎,持久化集群所有元信息
  2. 事务引擎:所有 DDL 操作的原子性保障,支持前进重试和回滚恢复
  3. DDL 处理流程:一条 DDL 从接收到执行完成的完整链路
  4. 定时器系统:驱动心跳检测、事务重试、流计算检查等周期性任务

核心概念速查表

概念 说明
SDB System Database,MNode 内部的元数据键值存储引擎
SDB 表 SDB 中按类型划分的元数据集合(如 dnode 表、database 表、超级表表等)
SDB Raw 元数据的序列化二进制格式,用于持久化和 Raft 复制
事务(Trans) DDL 操作的原子执行单元,包含 redo/undo/commit 三类动作列表
事务阶段 事务执行的状态机,从 PREPARE → REDO → COMMIT → FINISH
冲突检测 防止多个事务并发修改同一资源(数据库级或全局级)
vgId=1 MNode 的 Raft 组固定使用 vgId=1
sdb.dat SDB 全量快照文件,存储在 MNode 数据目录下

详细解析

1. SDB --- 元数据存储引擎

1.1 架构定位

SDB 是 MNode 的核心存储层,所有集群元数据(节点信息、数据库定义、超级表 Schema、VGroup 分布、用户权限、流计算规则等)都存储在 SDB 中。

复制代码
SDB 架构:

  ┌──────────────────────────────────────────────────┐
  │                    MNode 进程                      │
  │                                                    │
  │   ┌───────────────────────────────────────────┐   │
  │   │              SDB(内存)                    │   │
  │   │                                           │   │
  │   │  ┌─────────┐ ┌─────────┐ ┌─────────┐    │   │
  │   │  │ dnode 表 │ │ db 表   │ │ stb 表  │ ...  │   │
  │   │  │(哈希表) │ │(哈希表) │ │(哈希表) │    │   │
  │   │  └─────────┘ └─────────┘ └─────────┘    │   │
  │   │                                           │   │
  │   └──────────────┬────────────────────────────┘   │
  │                  │                                 │
  │         ┌────────┼────────┐                        │
  │         ▼        ▼        ▼                        │
  │     sdb.dat   WAL 日志  Raft 复制                   │
  │    (全量快照) (增量日志) (多副本同步)                 │
  └──────────────────────────────────────────────────┘

设计特点

  • 纯内存操作:所有元数据读取直接从内存哈希表查找,无磁盘 I/O
  • 按类型分表:每种元数据类型一个独立的哈希表,支持不同的键类型(整数键或字符串键)
  • 引用计数:读取元数据时获取引用,使用完毕后释放,防止读取到正在修改的数据
  • 读写锁:每张 SDB 表有独立的读写锁,读操作并发执行,写操作互斥
1.2 SDB 表类型全览

SDB 内部包含约 30 张元数据表,覆盖集群管理的所有维度:

分类 SDB 表 键类型 存储内容
集群管理 cluster INT64 集群 ID、名称、运行时间
dnode INT32 数据节点注册信息、硬件资源、在线状态
mnode INT32 管理节点副本信息、Raft 角色
qnode INT32 查询节点
snode INT32 流计算节点
anode INT32 AI 分析节点
数据管理 db STRING 数据库定义、全部配置参数
vgroup INT32 VGroup 分布、哈希范围、副本位置、负载统计
stb STRING 超级表 Schema(列定义、Tag 定义、压缩配置)
sma STRING SMA 索引定义(RSMA/TSMA)
idx STRING 自定义索引
用户权限 user STRING 用户名、密码哈希、权限映射
auth STRING 认证记录
acct STRING 账户信息、资源配额
订阅系统 topic STRING TMQ 主题定义(SQL/数据库/超级表订阅)
consumer INT64 消费者状态、分配的主题
subscribe STRING 订阅关系
offset STRING 消费位点
流计算 stream STRING 流计算任务定义
stream_ck --- 流计算检查点
stream_seq --- 流计算序列号
事务 trans INT32 事务记录(用于崩溃恢复)
其他 func STRING UDF 函数定义
view STRING 视图定义
arbgroup INT32 仲裁组信息
compact INT32 压缩任务
cfg STRING 动态配置
grant --- 授权日志
1.3 行的生命周期

SDB 中每行数据都有一个状态字段,反映其在事务中的生命周期位置:

复制代码
创建流程:  INIT → CREATING → READY(正常服务)
删除流程:  READY → DROPPING → DROPPED
更新流程:  READY → UPDATE → READY
  • INIT / CREATING:事务 PREPARE 阶段写入,表示正在创建中
  • READY:事务 COMMIT 阶段写入,表示可正常使用
  • DROPPING / DROPPED:删除事务的中间和最终状态
  • UPDATE:更新事务的中间状态

这种多阶段状态设计的好处是:事务中途失败时,可以根据行状态判断需要回滚还是前进。例如,状态为 CREATING 的行在回滚时直接删除即可。

1.4 持久化与复制

SDB 数据通过两种方式持久化:

WAL(Write-Ahead Log)

  • MNode 的 Raft 组(vgId=1)使用 WAL 记录每一条元数据变更
  • WAL 采用 fsync 模式强制刷盘,保证不丢数据
  • WAL 是增量日志,用于 Raft 日志复制和故障恢复

sdb.dat(全量快照)

  • 周期性地将整个 SDB 内存数据序列化写入 sdb.dat 文件
  • 写入有增量阈值控制------只有累积了足够多的变更才会触发写入
  • 启动时先加载 sdb.dat,再回放 WAL 中尚未包含在快照中的日志

Raft 复制

  • 所有 SDB 写入操作先封装为二进制格式,通过 Raft 协议提议(propose)
  • Leader 将提议复制到多数派 Follower 后,才将变更应用到内存
  • Follower 的 SDB 通过回放 Raft 提交的日志来保持与 Leader 一致
  • 当 Follower 落后过多时,Leader 发送完整 SDB 快照(snapshot)来同步

2. 事务引擎

2.1 为什么需要事务?

一个简单的 CREATE DATABASE 操作,实际上需要在多个节点上执行多个步骤:

  1. 在 SDB 中写入数据库元数据
  2. 在 SDB 中写入多个 VGroup 元数据
  3. 向多个 dnode 发送消息,要求它们创建对应的 vnode
  4. 确认所有 vnode 创建成功后,将元数据状态从 CREATING 改为 READY

如果步骤 3 中某个 dnode 不可达,整个操作需要原子性地成功或失败。这就是事务引擎的职责。

2.2 事务的构成

每个事务包含以下关键属性:

属性 说明
事务 ID 自增的唯一整数标识
当前阶段 事务状态机的当前位置(见下文)
冲突类型 决定与其他事务的冲突检测范围
执行策略 失败时重试还是回滚
执行模式 动作并行还是串行执行
动作列表 四组动作列表:prepare / redo / undo / commit

每个"动作"要么是一条 SDB 写入 (修改元数据),要么是一条 RPC 消息(发往 vnode 或 dnode)。

2.3 事务阶段状态机
复制代码
事务阶段流转:

                    成功
  PREPARE ──────→ REDO_ACTION ──────→ COMMIT ──────→ COMMIT_ACTION ──────→ FINISH
     │                 │                                                      ▲
     │                 │ 失败                                                  │
     │                 ▼                                                      │
     │            ROLLBACK ────→ UNDO_ACTION ──────→ PRE_FINISH ─────────────┘
     │
     └──→ 失败:直接结束(Raft 未提交,无需回滚)

各阶段说明:

阶段 说明
PREPARE 将事务记录和 prepare 动作通过 Raft 提议。Raft 提交后,事务持久化到多数派节点,保证即使 Leader 切换也不会丢失
REDO_ACTION 执行前进动作------向 dnode/vnode 发送创建/修改消息,或写入 SDB 记录。所有动作完成后进入 COMMIT
COMMIT 将 commit 动作列表中的 SDB 写入通过 Raft 提交,将元数据状态从中间态更新为最终态(如 CREATING → READY)
COMMIT_ACTION 执行提交后的善后动作(如果有的话)
FINISH 事务完成,从 SDB 中删除事务记录
ROLLBACK redo 失败且策略为回滚时的过渡阶段
UNDO_ACTION 执行回滚动作------向 dnode/vnode 发送删除/撤销消息,将 SDB 中间态记录清理
PRE_FINISH 回滚完成后的清理阶段
2.4 执行策略

事务创建时选择失败时的应对策略:

策略 行为 适用场景
重试(Retry) redo 动作失败时,等待定时器周期性重试,直到成功为止 CREATE DATABASE、CREATE STABLE 等大多数 DDL------目标节点可能临时不可达,重试即可
回滚(Rollback) redo 动作失败时,执行 undo 动作列表撤销所有已完成的操作 某些对一致性要求更严格的操作

重要 :采用重试策略的事务会无限重试(直到手动 KILL 或成功为止)。如果某个 dnode 永久下线,相关事务会一直处于 REDO_ACTION 阶段,阻塞同一冲突范围内的其他事务。此时需要管理员介入。

2.5 执行模式
模式 行为
并行(Parallel) 所有 redo/undo 动作同时发出,不等待前一个完成
串行(Serial) 动作逐个执行,前一个完成后才发送下一个
分组并行(Group Parallel) 同一组内并行,不同组之间串行

大多数 DDL 使用并行模式------例如 CREATE DATABASE 需要向多个 dnode 发送创建 vnode 的消息,这些消息可以同时发出。

2.6 冲突检测

为了防止并发 DDL 操作导致元数据不一致,事务引擎实施冲突检测:

冲突类型 范围 示例
无冲突 不与任何事务冲突 只读操作
全局冲突 与所有其他事务互斥 CREATE MNODE、DROP DNODE
数据库级冲突 与同一数据库的事务互斥 CREATE DATABASE、ALTER DATABASE
数据库内冲突 与同一数据库内同一超级表的事务互斥 CREATE STABLE、ALTER STABLE

当新事务提交时,先检查是否有冲突的进行中事务。如果有冲突,返回错误码 TSDB_CODE_MND_TRANS_CONFLICT,客户端需要稍后重试。

2.7 事务的崩溃恢复

事务本身也是 SDB 中的一张表(trans 表),通过 Raft 复制到多数派节点。这意味着:

  1. Leader 切换:新 Leader 上任后,从 SDB 中读取所有未完成的事务,自动继续执行
  2. MNode 重启 :从 sdb.dat + WAL 恢复 SDB 后,定时器自动拉起(pullup)未完成的事务
  3. 动作超时:每个动作有 15 分钟的超时限制,超时后标记失败并触发策略(重试或回滚)

定时器每 2 秒tsTransPullupInterval)检查一次是否有需要推进的事务。

2.8 管理事务
sql 复制代码
-- 查看当前正在执行的事务
SHOW TRANSACTIONS;

-- 输出示例:
-- id | create_time         | stage        | db    | type       | lasted
--  5 | 2024-01-15 10:30:00 | redoAction  | power | create-db  | 120s

-- 强制终止卡住的事务(跳过未完成的动作)
KILL TRANSACTION 5;

KILL TRANSACTION 有两种模式:

  • 跳过模式:将所有未完成的动作标记为"已完成",让事务直接走完剩余阶段
  • 中断模式:直接跳到 PRE_FINISH 阶段结束事务

警告:KILL TRANSACTION 是危险操作------它可能导致部分 dnode 上的 vnode 与 SDB 元数据不一致。只有在确认事务无法自行恢复时才使用,使用后需要检查集群状态。

3. DDL 处理全链路

3.1 总体流程

CREATE DATABASE power VGROUPS 4 REPLICA 3 为例,展示 DDL 的完整处理流程:

复制代码
DDL 全链路(以 CREATE DATABASE 为例):

  客户端                    MNode Leader                  DNode 1/2/3
    │                          │                              │
    │  1. 发送 CREATE DB 请求   │                              │
    │─────────────────────────→│                              │
    │                          │                              │
    │                          │  2. 反序列化,提取参数          │
    │                          │  3. 检查:DB 是否已存在?       │
    │                          │  4. 检查:用户权限              │
    │                          │  5. 检查:授权/License          │
    │                          │  6. 校验 DB 配置参数            │
    │                          │                              │
    │                          │  7. 分配 VGroup:              │
    │                          │     计算各 dnode 评分           │
    │                          │     选择评分最低的 dnode        │
    │                          │     分配 hash 范围             │
    │                          │                              │
    │                          │  8. 创建事务(重试策略,DB 级冲突)│
    │                          │                              │
    │                          │  9. 构建动作列表:              │
    │                          │     prepare: DB+VGroup 写入 SDB(CREATING 状态)
    │                          │     redo:    向 dnode 发消息创建 vnode
    │                          │     undo:    向 dnode 发消息删除 vnode
    │                          │     commit:  DB+VGroup 写入 SDB(READY 状态)
    │                          │                              │
    │                          │  10. PREPARE 阶段:            │
    │                          │      通过 Raft 提议事务记录      │
    │                          │      Raft 多数派确认后,         │
    │                          │      DB 和 VGroup 以 CREATING   │
    │                          │      状态出现在 SDB 中          │
    │                          │                              │
    │  11. 返回"操作进行中"     │                              │
    │←─────────────────────────│                              │
    │                          │                              │
    │                          │  12. REDO 阶段:               │
    │                          │      向 dnode 1/2/3 发送        │
    │                          │      创建 vnode 消息(并行)     │
    │                          │─────────────────────────────→│
    │                          │                              │
    │                          │  13. 收到所有 dnode 的成功响应   │
    │                          │←─────────────────────────────│
    │                          │                              │
    │                          │  14. COMMIT 阶段:             │
    │                          │      通过 Raft 提议:           │
    │                          │      DB → READY                │
    │                          │      VGroup → READY            │
    │                          │                              │
    │                          │  15. FINISH:删除事务记录       │
    │                          │                              │
    │  16. 异步通知客户端成功    │                              │
    │←─────────────────────────│                              │
3.2 不同 DDL 的事务构成
DDL 操作 冲突级别 执行策略 Redo 动作 Commit 动作
CREATE DATABASE DB 级 重试 向 dnode 发消息创建 vnode DB+VGroup → READY
DROP DATABASE DB 级 重试 向 dnode 发消息删除 vnode DB+VGroup → DROPPED
ALTER DATABASE DB 级 重试 向 dnode 发消息更新 vnode 配置 DB → READY(新配置)
CREATE STABLE DB 内 重试 向 vnode 发消息创建超级表 STB → READY
ALTER STABLE DB 内 重试 向 vnode 发消息更新 Schema STB → READY(新 Schema)
DROP STABLE DB 内 重试 向 vnode 发消息删除超级表 STB → DROPPED
CREATE USER 全局 回滚 SDB 写入用户记录 User → READY
CREATE MNODE 全局 重试 向目标 dnode 发消息创建 mnode MNode → READY
DROP DNODE 全局 重试 迁移该节点上的所有 vnode DNode → DROPPED
BALANCE VGROUP DB 级 重试 向 dnode 发消息迁移 vnode VGroup → READY(新分布)
3.3 错误处理与重试

当 redo 动作(RPC 消息)失败时:

复制代码
动作失败处理:

  动作发送 → dnode 响应失败(超时/拒绝/不可达)
      │
      ├── 策略=重试:
      │     标记动作为"未完成"
      │     事务保持在 REDO_ACTION 阶段
      │     等待下一次定时器触发(每 2 秒)
      │     重新发送未完成的动作
      │     (无限重试,直到成功或被 KILL)
      │
      └── 策略=回滚:
            事务进入 ROLLBACK → UNDO_ACTION
            发送 undo 动作撤销已完成的 redo
            所有 undo 完成后 → PRE_FINISH → FINISH

动作超时:每个动作有 15 分钟的超时限制。超时后自动标记为失败,触发重试或回滚。

4. MNode 部署与高可用

4.1 MNode 副本

MNode 最多 3 个副本,通过 Raft 协议保持一致性:

  • vgId=1:MNode 的 Raft 组固定使用 vgId=1
  • Leader:处理所有写操作(DDL),协调事务执行
  • Follower:被动接收 Raft 日志,保持元数据同步
  • 只读转发:Follower 收到写请求时,返回 Leader 地址,客户端自动重定向
sql 复制代码
-- 查看 MNode 状态
SHOW MNODES;

-- 输出示例:
-- id | endpoint       | role     | role_time           | create_time
--  1 | node1:6030     | leader   | 2024-01-15 10:00:00 | 2024-01-01 00:00:00
--  2 | node2:6030     | follower | 2024-01-15 10:00:00 | 2024-01-02 00:00:00
--  3 | node3:6030     | follower | 2024-01-15 10:00:00 | 2024-01-03 00:00:00

-- 创建 MNode 副本
CREATE MNODE ON DNODE 2;
CREATE MNODE ON DNODE 3;

-- 删除 MNode 副本
DROP MNODE ON DNODE 3;
4.2 Leader 选举与切换

MNode 使用 Raft 协议进行 Leader 选举:

  1. 正常运行:Leader 定期向 Follower 发送心跳
  2. Leader 失联 :Follower 在选举超时(tsMnodeElectIntervalMs)后发起选举
  3. 投票:候选者向其他节点发送投票请求,获得多数票者成为新 Leader
  4. 恢复执行:新 Leader 上任后,自动拉起所有未完成的事务继续执行
4.3 Leader 切换后的恢复

新 Leader 的恢复过程:

  1. Raft 日志回放 :回放 WAL 中未包含在 sdb.dat 快照中的日志条目
  2. SDB 恢复标记:标记恢复完成,允许 SDB 接受新的写入
  3. 事务拉起:扫描 SDB 中所有未完成的事务(trans 表),根据各事务当前阶段继续执行
  4. 状态广播:更新流计算等模块的执行信息

注意:Follower 不执行事务、不处理 DDL、不运行定时任务。所有这些工作都由 Leader 独占。

4.4 快照同步

当 Follower 落后太多(WAL 日志已被回收),Leader 通过快照同步来恢复:

  1. Leader 将整个 SDB 内存数据序列化为二进制流
  2. 分块发送给落后的 Follower
  3. Follower 用收到的快照替换本地 SDB
  4. 后续通过增量 Raft 日志继续同步

5. 定时器系统

MNode 有两个定时线程,驱动所有周期性任务。只有 Leader 节点执行这些任务,Follower 节点跳过。

5.1 秒级定时器

每 1 秒触发一次,处理以下任务:

任务 默认间隔 说明
事务拉起 2 秒 检查并推进所有未完成的事务
TMQ 消费者重平衡 2 秒 检查消费者组是否需要重新分配
压缩任务检查 10 秒 检查进行中的压缩任务状态
TTL 过期清理 10 秒 向 vnode 发送清理过期子表的指令
流计算检查点 30 秒 触发流计算状态持久化
流计算共识 30 秒 检查流计算跨节点一致性
授权心跳 60 秒 检查 License 有效性
流节点健康检查 240 秒 检查流计算节点存活状态
集群运行时间 300 秒 更新集群 uptime 计数器
VDB 裁剪 3600 秒 触发 vnode 数据库空间回收
S3 迁移 3600 秒 触发数据迁移到对象存储
遥测上报 86400 秒 发送匿名使用统计(可关闭)
5.2 毫秒级定时器

每 100 毫秒触发一次,处理需要更高时间精度的任务:

任务 默认间隔 说明
仲裁心跳 2000 毫秒 向仲裁组发送心跳
仲裁同步检查 3000 毫秒 检查仲裁组数据同步状态
DNode 在线检测 5000 毫秒 检查各 dnode 心跳是否超时
Raft 提议超时检查 30 秒 检查是否有卡住的 Raft 提议
快照发送重试 可配置 重试失败的快照发送
5.3 定时器与事务的协作

定时器系统是事务引擎的"心跳驱动"------没有定时器,失败的事务永远不会被重试。协作流程:

复制代码
定时器驱动事务执行:

  每 2 秒 → 扫描 trans 表
    │
    ├── 事务 A(REDO_ACTION 阶段,有未完成动作)
    │     → 重新发送未完成的 RPC 消息
    │
    ├── 事务 B(COMMIT 阶段)
    │     → 通过 Raft 提议 commit 日志
    │
    └── 事务 C(FINISH 阶段)
          → 从 SDB 中删除事务记录

6. MNode 初始化顺序

MNode 启动时,按照严格的依赖顺序初始化各个模块:

复制代码
初始化顺序(依赖关系从上到下):

  1. WAL              ← 日志存储基础
  2. SDB              ← 元数据存储引擎
  3. Trans            ← 事务引擎
  4. Cluster          ← 集群基本信息
  5. MNode / QNode / SNode / ANode  ← 逻辑节点管理
  6. ArbGroup / Config  ← 仲裁和配置
  7. DNode            ← 数据节点管理
  8. User / Grant / Privilege / Acct  ← 用户权限体系
  9. Stream / Topic / Consumer / Subscribe  ← 订阅和流计算
  10. VGroup / STB / SMA / Index  ← 数据对象
  11. InfoSchema / PerfSchema  ← 系统表
  12. DB               ← 数据库管理
  13. Func / View / Compact  ← 功能模块
  14. ← 加载 sdb.dat 恢复持久化数据 →
  15. Profile / Show / Query  ← 查询和展示
  16. Sync             ← Raft 协议启动
  17. Telemetry        ← 遥测(最后启动)

关键点:SDB 的加载(读取 sdb.dat 文件)在模块注册之后、Sync 启动之前完成。这确保了所有表的回调函数都已注册,可以正确反序列化各种类型的元数据。

7. 消息处理机制

7.1 消息分发

MNode 维护一个消息类型到处理函数的映射表。各模块在初始化时注册自己关心的消息类型。收到请求时,MNode 按以下流程处理:

复制代码
消息处理流程:

  RPC 请求到达
      │
      ▼
  查找消息类型对应的处理器
      │
      ├── 未注册 → 返回错误
      │
      ▼
  检查 MNode 状态
      │
      ├── 未就绪(正在恢复/非 Leader)→ 返回重定向或错误
      │
      ▼
  调用处理器执行业务逻辑
      │
      ├── 同步返回 → 直接回复客户端
      └── 异步(创建事务)→ 返回"操作进行中"
7.2 消息队列

不同类型的消息进入不同的队列,实现隔离和优先级控制:

队列 处理内容
写队列 DDL 操作(CREATE/ALTER/DROP)、定时器触发的写任务
读队列 元数据查询(SHOW 命令)、遥测、流节点检查
同步队列 Raft 协议消息(选举、日志复制、心跳)
应用队列 Raft 提交的日志条目应用到 SDB
仲裁队列 仲裁心跳和同步检查
7.3 事务响应处理

当 dnode/vnode 完成事务动作后,会发送响应消息回 MNode。MNode 的处理流程:

  1. 从响应中提取事务 ID 和动作 ID
  2. 在 SDB 的 trans 表中查找对应事务
  3. 将该动作标记为"已完成"
  4. 检查当前阶段的所有动作是否都已完成
  5. 如果全部完成,推进事务到下一阶段

代码示例

观察事务执行过程

sql 复制代码
-- 创建一个大型数据库(多 VGroup),观察事务过程
CREATE DATABASE bigdb VGROUPS 100 REPLICA 3;

-- 立即查看事务状态
SHOW TRANSACTIONS;
-- 可以看到 create-db 事务处于 redoAction 阶段
-- 因为需要向多个 dnode 发送创建 vnode 的消息

-- 等待几秒后再次查看
SHOW TRANSACTIONS;
-- 事务完成后,列表为空

模拟事务冲突

sql 复制代码
-- 终端 1:执行一个耗时的 DDL
ALTER DATABASE power WAL_RETENTION_PERIOD 3600;

-- 终端 2:同时执行同一数据库的 DDL(会冲突)
ALTER DATABASE power CACHEMODEL 'both';
-- 可能返回错误:Transaction conflict
-- 需要等终端 1 的事务完成后重试

处理卡住的事务

sql 复制代码
-- 查看是否有长时间未完成的事务
SHOW TRANSACTIONS;

-- 如果某事务的 lasted 时间异常长(如超过 10 分钟)
-- 检查相关 dnode 是否在线
SHOW DNODES;

-- 如果 dnode 已永久下线,需要先处理 dnode
-- 然后考虑 KILL 卡住的事务
KILL TRANSACTION <trans_id>;

-- KILL 后检查集群状态
SHOW power.VGROUPS;

MNode 高可用部署

sql 复制代码
-- 查看当前 MNode 状态
SHOW MNODES;

-- 在三个不同节点上部署 MNode
CREATE MNODE ON DNODE 2;
CREATE MNODE ON DNODE 3;

-- 验证 3 副本状态
SHOW MNODES;
-- 应该看到 1 个 leader + 2 个 follower

-- 模拟 Leader 故障恢复:
-- 停止 leader 所在节点后,几秒内会自动选出新 leader
-- 集群 DDL 操作短暂不可用后自动恢复

性能考量

事务相关参数

参数 默认值 说明
tsTransPullupInterval 2 秒 事务重试检查间隔。减小可加快重试速度,但增加 CPU 开销
动作超时 15 分钟 单个动作的超时时间。超时后标记失败并重试
Raft 提议超时 60 秒 通过 Raft 提议的超时时间

MNode 选举与心跳参数

参数 说明
tsMnodeElectIntervalMs Raft 选举超时。减小可更快检测 Leader 故障,但增加误选风险
tsMnodeHeartbeatIntervalMs Raft 心跳间隔。与选举超时配合,通常为选举超时的 1/3~1/5

SDB 性能特点

方面 特点
读取性能 O(1) 哈希查找,纯内存操作,极快
写入性能 受 Raft 多数派确认延迟限制,通常几毫秒到几十毫秒
DDL 吞吐 受事务冲突限制------同一数据库的 DDL 串行执行
内存占用 与元数据总量成正比------百万张表的超级表 Schema 可能占用数 GB

DDL 性能优化建议

  1. 避免频繁 ALTER:每次 ALTER STABLE 都需要向所有 vnode 推送新 Schema,VGroup 越多延迟越高
  2. 合理设置 VGROUPS:VGroup 数量直接影响 CREATE DATABASE 事务的动作数量
  3. 批量操作 :需要创建大量子表时,使用 INSERT INTO ... USING ... TAGS ... 自动建表,而非逐个 CREATE TABLE
  4. 避免 DDL 并发:同一数据库的 DDL 因冲突检测机制而串行,客户端不应并发发送

FAQ

Q1: MNode 和 VNode 的关系是什么?

MNode 负责元数据管理 (数据库定义、表 Schema、用户权限、VGroup 分布等),VNode 负责数据存储和查询。MNode 不存储任何时序数据。客户端写入/查询时先通过 MNode 获取路由信息(表在哪个 VGroup),然后直接与 VNode 通信。

Q2: 为什么 DDL 操作有时候很慢?

DDL 操作需要:(1) 通过 Raft 持久化元数据变更,(2) 向多个 dnode 发送消息并等待响应。如果某个 dnode 负载高或网络延迟大,整个事务会等待。VGroup 数量越多,需要发送的消息越多。可以通过 SHOW TRANSACTIONS 观察事务当前阶段和耗时。

Q3: "Transaction conflict" 错误怎么处理?

这表示你的 DDL 操作与一个正在执行的事务冲突了。常见场景:

  • 同时对同一数据库执行多个 ALTER 操作
  • 上一个 DDL 还未完成就发起了新的 DDL

解决方法:等待当前事务完成后重试。使用 SHOW TRANSACTIONS 查看进行中的事务。

Q4: MNode Leader 切换后数据会丢失吗?

不会。所有元数据变更都通过 Raft 协议复制到多数派节点后才确认。Leader 切换后,新 Leader 从本地 SDB + WAL 恢复完整状态,并自动继续执行未完成的事务。

Q5: 可以只部署 1 个 MNode 吗?

可以,但不推荐用于生产环境。单 MNode 是单点故障------如果该节点宕机,所有 DDL 操作和元数据查询都不可用(已有数据的读写不受影响,因为客户端有缓存路由信息)。生产环境建议部署 3 个 MNode 副本。

Q6: SDB 的 sdb.dat 文件损坏了怎么办?

如果 sdb.dat 损坏,MNode 启动时会报错。恢复方式:

  1. 如果有其他 MNode 副本,可以从健康的副本同步(通过 Raft 快照)
  2. 删除损坏的 sdb.dat,MNode 会通过 WAL 回放重建 SDB(如果 WAL 完整)
  3. 极端情况下,从其他 MNode 副本手动复制数据目录

Q7: MNode 的定时任务会不会影响 DDL 性能?

定时任务(事务拉起、TMQ 重平衡、TTL 清理等)与 DDL 共享写队列,但通常不会造成阻塞,因为:

  • 大多数定时任务执行很快(微秒级别的检查)
  • 只有需要创建新事务的定时任务(如 TTL 清理)才会占用写队列
  • Follower 节点不执行任何定时任务,不影响 Leader

Q8: 如何监控 MNode 的健康状态?

sql 复制代码
-- 检查 MNode 角色和状态
SHOW MNODES;

-- 检查是否有卡住的事务
SHOW TRANSACTIONS;

-- 检查集群整体状态
SHOW CLUSTER ALIVE;

-- 通过 taosKeeper + Grafana 监控:
-- - MNode Leader 切换次数
-- - 事务执行耗时
-- - SDB 表行数变化

关于 TDengine

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

相关推荐
ApacheSeaTunnel1 小时前
AI 让 SeaTunnel 读源码和调试过时了吗?
大数据·ai·开源·数据集成·seatunnel·技术分享·数据同步
这个DBA有点耶1 小时前
数据库上云 vs 自建:从成本到人力的三维对比与决策框架
数据库·经验分享·sql·创业创新·dba
白鲸开源1 小时前
杀疯了!SeaTunnel AI CLI 解锁数据集成新玩法
大数据·人工智能·github
shizhan_cloud1 小时前
MySQL 索引优化 + 慢查询日志
数据库·mysql
Drache_long1 小时前
MySQL数据库(故障排除)
数据库·mysql
2303_821287381 小时前
如何清洗SQL输入数据_使用框架内置的ORM处理数据交互
jvm·数据库·python
清风雅雨1 小时前
AI编程:OA流程明细表中多个金额字段由整数改为2位小数
数据库·ai编程
菜鸟上路_lbz1 小时前
sqlserver存储过程查询缓慢锁表分析
数据库·sqlserver
Elastic 中国社区官方博客1 小时前
在 Elasticsearch 中使用利润率与流行度加权来优化电商搜索
大数据·数据库·elasticsearch·搜索引擎·全文检索