TDengine 写入内部流程 — 全链路与故障诊断手册

5.写入路径 > 04 写入内部流程


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

概述

本文是 写入路径模块的诊断核心:从客户端到 VNode、WAL、MemTable 的完整链路,以及每个检查点可能产生的错误。读完本文,大模型应能:

  1. 说出一条写入请求经过哪些阶段、每阶段做什么校验;
  2. 根据 错误消息 / 错误码 / 现象,定位失败发生在哪一阶段;
  3. 给出可操作的排查步骤(查什么系统表、改什么配置、客户端如何重试)。

与其他四篇的关系

篇章 职责
01 SQL INSERT INSERT 语法、约束、INSERT 专属错误
02 Schemaless 三种协议、自动建表、协议专属错误
03 STMT 绑定 高性能写入 API、Bind 专属错误
04 本文 全链路机制 + 跨入口统一诊断
05 删除与更新 DELETE/UPDATE 语义与错误

核心概念

概念 说明
写入入口 SQL INSERT、Schemaless、STMT/STMT2、REST(经 taosAdapter)、流计算 Sink
Catalog 客户端元数据缓存:表 Schema、VGroup 路由、Leader 地址
SUBMIT 消息 客户端发往 VNode Leader 的二进制写入包
Raft 提议 Leader 将写入作为日志条目复制到 Follower
WAL 写前日志,崩溃恢复依据
MemTable 内存跳表,写入立即可查
Commit MemTable 刷盘成 TSDB 文件(异步,与"返回成功"分离)

五种写入入口对比

入口 典型场景 是否预建表 SQL 解析 性能
SQL INSERT 应用 SQL、ETL 可选 USING 自动建表 每次(批量可摊薄)
Schemaless IoT、Telegraf、Influx 迁移 自动 协议解析 中高
STMT/STMT2 高并发持续写入 可选 USING Prepare 一次 最高
REST(6041) HTTP 集成 同 Schemaless/SQL 经 taosAdapter
流计算 Sink CREATE STREAM 输出 自动 内部生成

诊断要点 :无论哪种入口,到达 VNode 之后的路径相同(WAL → MemTable → Raft 复制 → 返回)。因此"写入慢/卡住/部分失败"的排查,入口差异只影响前 4 步。

详细解析

1. 写入全链路(14 步)

INSERT INTO d1001 VALUES (now, 10.3, 219, 0.31) 为例:

复制代码
┌──────────────────────────────────────────────────────────────────────┐
│ 阶段 A:客户端(taosc / JDBC / taospy / taosAdapter)                  │
├──────────────────────────────────────────────────────────────────────┤
│ ① 接收请求(SQL 文本 / Line 协议 / Bind 参数)                         │
│ ② 解析:提取库名、表名、列值、时间戳                                   │
│ ③ Catalog:查表 Schema、VGroup、Leader(缓存未命中则问 MNode)          │
│ ④ 编码:按列类型序列化为 SUBMIT 二进制                                 │
│ ⑤ RPC:按 VGroup 分组,发往对应 Leader                                 │
└───────────────────────────────┬──────────────────────────────────────┘
                                │ TCP RPC
┌───────────────────────────────▼──────────────────────────────────────┐
│ 阶段 B:VNode Leader                                                  │
├──────────────────────────────────────────────────────────────────────┤
│ ⑥ RPC 接收,入写队列                                                   │
│ ⑦ 校验:表存在性、Schema 版本、权限、时间戳范围、列类型                 │
│ ⑧ Raft 提议:写 WAL → 并行复制到 Follower → 等多数派 Ack               │
│ ⑨ 应用状态机:自动建表(USING)、写 MemTable、更新 Last 缓存            │
│ ⑩ 触发副作用:Stream、RSMA(如有)                                     │
│ ⑪ 构建响应(影响行数 / 错误码)                                        │
└───────────────────────────────┬──────────────────────────────────────┘
                                │ RPC 响应
┌───────────────────────────────▼──────────────────────────────────────┐
│ 阶段 C:客户端收尾                                                     │
├──────────────────────────────────────────────────────────────────────┤
│ ⑫ 解析响应:成功 / 可重试错误 / 不可重试错误                           │
│ ⑬ 可重试时:刷新 Catalog、换 Leader 地址、退避重试                     │
│ ⑭ 返回给应用                                                           │
└──────────────────────────────────────────────────────────────────────┘

        ═══ 异步(不阻塞写入返回)═══
        MemTable 满 → Flush → .data/.stt 文件 → 清理旧 WAL
1.1 批量与跨表写入的拆分

一条 SQL 写多表时,客户端按 VGroup 拆成多个 SUBMIT 包并行发送:

复制代码
INSERT INTO d001 VALUES (...), d002 VALUES (...), d003 VALUES (...)

  hash(d001) → VGroup 1  ─┐
  hash(d002) → VGroup 1  ─┼→ RPC 包 #1(并行)
  hash(d003) → VGroup 2  ───→ RPC 包 #2(并行)

  全部成功 → 返回总影响行数
  任一失败 → 整批 SQL 报错(单条 INSERT 语句的原子性)

诊断含义:跨 VGroup 批量写入中,若只有一个 VGroup 的 VNode 故障,整批失败;错误消息可能指向具体 VGroup。

1.2 VGroup 路由
复制代码
hash = murmurHash(库名 + 表名)   // 可受 TABLE_PREFIX / TABLE_SUFFIX 影响
vgroup_id = 按 hash 落入的分段

客户端可本地计算 → 无需每次查 MNode
VGroup 迁移 / 扩容后 → Catalog 过期 → 客户端自动刷新并重试

2. 各阶段校验项与失败表现

阶段 校验内容 典型错误消息 错误码(十六进制)
A-② 解析 SQL/协议语法、列数 syntax error near 0x80000216
A-② 解析 SQL 超长 SQL statement too long 0x8000021A
A-③ Catalog 库未选 / 库不存在 Database not specified / Database not exist 0x8000021B / 0x80000220
A-③ Catalog 表不存在且无 USING Table does not exist 0x8000027C
A-③ Catalog Tag 数量不匹配 Tags number not matched 0x80000296
A-⑤ RPC 网络不可达 Unable to establish connection 0x80000101
A-⑤ RPC 超时 Conn read timeout 0x80000105
A-⑤ RPC 节点不可用 some vnode(s) out of service 0x80000106
B-⑦ 权限 无写权限 No write permission / Database write operation denied 0x80000219 / 0x80000522
B-⑦ Schema 列类型不匹配 Invalid value type 0x80000292
B-⑦ Schema 列数不对 Illegal number of columns 0x800002A4
B-⑦ Schema 值超长 Value too long for column/tag 0x800002B9
B-⑦ 时间戳 超出 KEEP / 未来过多 Timestamp data out of range 0x80000B22
B-⑦ 时间戳 格式非法 Incorrect TIMESTAMP value 0x800002A7
B-⑧ Raft 非 Leader / 切换中 Sync leader is unreachable 0x80000903
B-⑧ Raft 同步超时 Sync timeout 0x80000901
B-⑧ Raft 写阻塞 Sync write stall 0x80000908
B-⑧ 磁盘 空间不足 Out of disk space 0x80000009
B-⑧ WAL VNode 快照中禁写 Vnode write is disabled for snapshot 0x80000529

Schemaless 在阶段 A-② 额外可能出现的错误见《02 Schemaless 写入》;STMT Bind 在 A-② 见《03 STMT 绑定》。

3. 写入故障诊断决策树

用户描述的现象 自上而下排查:
#mermaid-svg-EQdKaOZqaBps5vCN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EQdKaOZqaBps5vCN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EQdKaOZqaBps5vCN .error-icon{fill:#552222;}#mermaid-svg-EQdKaOZqaBps5vCN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EQdKaOZqaBps5vCN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EQdKaOZqaBps5vCN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EQdKaOZqaBps5vCN .marker.cross{stroke:#333333;}#mermaid-svg-EQdKaOZqaBps5vCN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EQdKaOZqaBps5vCN p{margin:0;}#mermaid-svg-EQdKaOZqaBps5vCN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EQdKaOZqaBps5vCN .cluster-label text{fill:#333;}#mermaid-svg-EQdKaOZqaBps5vCN .cluster-label span{color:#333;}#mermaid-svg-EQdKaOZqaBps5vCN .cluster-label span p{background-color:transparent;}#mermaid-svg-EQdKaOZqaBps5vCN .label text,#mermaid-svg-EQdKaOZqaBps5vCN span{fill:#333;color:#333;}#mermaid-svg-EQdKaOZqaBps5vCN .node rect,#mermaid-svg-EQdKaOZqaBps5vCN .node circle,#mermaid-svg-EQdKaOZqaBps5vCN .node ellipse,#mermaid-svg-EQdKaOZqaBps5vCN .node polygon,#mermaid-svg-EQdKaOZqaBps5vCN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EQdKaOZqaBps5vCN .rough-node .label text,#mermaid-svg-EQdKaOZqaBps5vCN .node .label text,#mermaid-svg-EQdKaOZqaBps5vCN .image-shape .label,#mermaid-svg-EQdKaOZqaBps5vCN .icon-shape .label{text-anchor:middle;}#mermaid-svg-EQdKaOZqaBps5vCN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EQdKaOZqaBps5vCN .rough-node .label,#mermaid-svg-EQdKaOZqaBps5vCN .node .label,#mermaid-svg-EQdKaOZqaBps5vCN .image-shape .label,#mermaid-svg-EQdKaOZqaBps5vCN .icon-shape .label{text-align:center;}#mermaid-svg-EQdKaOZqaBps5vCN .node.clickable{cursor:pointer;}#mermaid-svg-EQdKaOZqaBps5vCN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EQdKaOZqaBps5vCN .arrowheadPath{fill:#333333;}#mermaid-svg-EQdKaOZqaBps5vCN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EQdKaOZqaBps5vCN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EQdKaOZqaBps5vCN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EQdKaOZqaBps5vCN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EQdKaOZqaBps5vCN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EQdKaOZqaBps5vCN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EQdKaOZqaBps5vCN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EQdKaOZqaBps5vCN .cluster text{fill:#333;}#mermaid-svg-EQdKaOZqaBps5vCN .cluster span{color:#333;}#mermaid-svg-EQdKaOZqaBps5vCN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EQdKaOZqaBps5vCN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EQdKaOZqaBps5vCN rect.text{fill:none;stroke-width:0;}#mermaid-svg-EQdKaOZqaBps5vCN .icon-shape,#mermaid-svg-EQdKaOZqaBps5vCN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EQdKaOZqaBps5vCN .icon-shape p,#mermaid-svg-EQdKaOZqaBps5vCN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EQdKaOZqaBps5vCN .icon-shape .label rect,#mermaid-svg-EQdKaOZqaBps5vCN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EQdKaOZqaBps5vCN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EQdKaOZqaBps5vCN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EQdKaOZqaBps5vCN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 报错






磁盘
权限

慢/卡住




写入出问题
报错还是慢/无响应?
错误消息含 Table does not exist?
加 USING 自动建表或先 CREATE TABLE
含 Timestamp out of range?
查 KEEP 配置; 检查客户端时区与精度
含 Sync timeout / leader unreachable?
查 ins_vgroups 状态; 等 Leader 选举完成; 客户端重试
含 Out of disk space / No write permission?
df 查数据目录; 扩盘或删过期库
GRANT WRITE ON db TO user
对照第二节错误码表; 查 taosdlog 对应 QID
WAL_LEVEL=2 且 fsync 频繁?
批量加大; 或 WAL_LEVEL=1 权衡持久性
副本数>=3 且跨机房?
网络 RTT 主导; 减副本或同机房部署
查 ins_database_stats 写入 TPS; 查 BUFFER 是否过小导致 Flush 频繁

4. 按症状分类诊断

4.1 写入完全失败(立即返回错误)
症状 最可能原因 排查步骤
Table does not exist 子表未建且 SQL 无 USING SHOW TABLES LIKE 'd%';加 USING stb TAGS(...)
Database not specified 连接串未带 db 且 SQL 无库前缀 USE dbname 或连接 URL 指定 database
Tags number not matched USING 的 TAGS 个数与超级表 Tag 列数不一致 DESCRIBE stb_name 数 Tag 列
Timestamp data out of range ts 早于 KEEP 或远超当前时间 SHOW CREATE DATABASE db 看 KEEP;核对 NTP
No write permission 用户无 WRITE 权限 SHOW GRANTS FOR user
Sync timeout VGroup 多数副本不可用 SELECT * FROM information_schema.ins_vgroups WHERE db_name='x'
Out of disk space 数据目录满 SELECT * FROM information_schema.ins_disk_usage
4.2 写入间歇性失败(有时成功有时失败)
症状 最可能原因 排查步骤
集群写入偶发超时 Leader 切换、网络抖动 客户端开启自动重试;查 ins_dnodes 是否 status=offline
批量写入随机失败 单批过大触发 RPC 限制 减小单批行数(建议 500~5000);检查 maxSQLLength
Schemaless 偶发类型错误 同 measurement 字段类型不一致 统一设备上报 schema;见 02 篇 Schema 演化节
4.3 写入返回成功但查不到数据
症状 最可能原因 排查步骤
INSERT 成功 SELECT 为空 写错库/表;或 ts 不在查询范围 确认 USE 的库;SELECT * FROM tb WHERE ts=写入的ts
刚写入查不到 查询连接到了不同节点且缓存未刷新 同一连接写入后立即查;Last 缓存模式影响"最新值"
部分列 NULL 指定列写入时未写到的列为 NULL 正常行为;检查 (ts, col1, col2) 是否漏列
4.4 写入很慢
症状 最可能原因 优化方向
单行 INSERT RPS 极低 每条一次 RPC + 解析 改批量 INSERT 或 STMT
批量仍慢 WAL fsync、副本同步 WAL_LEVEL=1;同机房部署;STRICT=off 弱一致(需评估)
首次 Schemaless 慢 自动建表 + Schema 推断 预热后稳定;或预建超级表改 SQL/STMT
CPU 高但 RPS 低 过小批量 + 过多连接 加大 batch;连接数 ≈ CPU 核数
4.5 重复时间戳行为(非错误)

同一子表写入 相同 ts 时,TDengine v3 用新行覆盖旧行(等同 Upsert),不是报错:

sql 复制代码
INSERT INTO d1001 VALUES ('2018-10-03 14:38:05', 10.3, 219, 0.31);
INSERT INTO d1001 (ts, current) VALUES ('2018-10-03 14:38:05', 22);
-- 查询 current = 22,其余列保留原值(部分列更新语义)

注意 :超级表上做窗口聚合/部分时序函数时,子表间合并后可能出现重复 ts 导致 查询报错(与写入无关),见 FAQ。

5. 持久性层级与"写入成功"的含义

客户端收到 成功 表示:

复制代码
✓ 数据已写入 Leader 的 WAL(WAL_LEVEL≥1)
✓ 已获 Raft 多数派确认(副本数决定 quorum)
✓ 已写入 MemTable(立即可查)

✗ 尚未保证已 Flush 到 .data 文件(异步)
✗ Follower 落后时仍可能成功(取决于 STRICT 配置)
WAL_LEVEL fsync 宕机最多丢
0 无 WAL 全部未落盘数据
1 OS 缓冲 极少(OS 崩溃窗口)
2 周期性 fsync 最多一个 fsync 周期

6. 监控与日志排查

6.1 系统表
sql 复制代码
-- 库级写入统计
SELECT db_name, write_rows, write_speed FROM information_schema.ins_database_stats;

-- VGroup 健康(status: leader/follower/offline)
SELECT db_name, vgroup_id, status, dnodes, tables, vnodes
FROM information_schema.ins_vgroups
WHERE db_name = 'power';

-- WAL 占用
SELECT database, vgroup_id, wal_level, files, create_time
FROM information_schema.ins_wals;

-- 磁盘
SELECT * FROM information_schema.ins_disk_usage;
6.2 日志关键词(taosdlog)
关键词 含义
vgId:* duplicate write request 客户端重复提交同一 Raft 版本(可忽略或检查重试逻辑)
Sync timeout 副本同步超时,写入失败
Sync write stall 复制窗口满,写入背压
Out of disk space 磁盘满
Timestamp data out of range 时间戳校验失败
Vnode write is disabled for snapshot 快照期间暂停写入,稍后重试

客户端日志中的 QID(Query ID) 可与 taosdlog 对齐,精确定位一次失败请求。

7. 关键配置与写入关系

参数 位置 对写入的影响
BUFFER 库级 MemTable 大小;过小 → Flush 频繁 → 写入抖动
WAL_LEVEL 库级 0/1/2,直接影响持久性与延迟
WAL_FSYNC_PERIOD 库级 LEVEL=2 时 fsync 间隔(ms)
REPLICA 库级 副本数越多,同步开销越大
STRICT 库级 强一致 vs 弱一致,影响写入等待策略
KEEP 库级 超出保留期的时间戳被拒绝
maxSQLLength taos.cfg 单条 SQL 最大长度(默认 1MB)
maxInsertBatchRows taos.cfg 单次 INSERT 最大行数

代码示例

验证写入是否到达 MemTable

sql 复制代码
CREATE DATABASE IF NOT EXISTS diag_test;
CREATE STABLE IF NOT EXISTS diag_test.meters (
  ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT
) TAGS (location BINARY(64), group_id INT);

INSERT INTO diag_test.d001 USING diag_test.meters TAGS('Beijing', 1)
  VALUES (NOW, 10.1, 220, 0.5);

-- 预期:立即查到 1 行(无需等 Flush)
SELECT * FROM diag_test.d001 ORDER BY ts DESC LIMIT 1;

VGroup 故障时自查

sql 复制代码
-- 若写入报 Sync timeout,先看 VGroup 是否 offline
SELECT vgroup_id, status, dnodes
FROM information_schema.ins_vgroups
WHERE db_name = 'diag_test' AND status != 'leader';

性能考量

阶段 典型耗时占比(批量 1000 行) 调优杠杆
客户端解析 ~5% STMT Prepare 缓存
网络 RPC ~10% 批量、同机房
WAL ~15% WAL_LEVEL、fsync 周期
MemTable ~30% BUFFER 加大
副本同步 ~35% REPLICA、STRICT、网络
响应 ~5% ---

Flush 到磁盘 不计入 写入 RTT,但 BUFFER 过小会导致 MemTable 切换频繁,间接造成 Sync write stall

FAQ

Q1: 写入成功是否意味着数据已在磁盘文件里?

不是。成功只保证 WAL + MemTable + 多数派复制。.data 文件由后台 Flush 异步生成。进程崩溃后,未 Flush 的数据可从 WAL 恢复。

Q2: 大模型常答错:TDengine 不允许重复时间戳?

错。同一子表相同 ts 允许写入 ,新数据 覆盖 旧数据(部分列更新)。重复 ts 导致 查询报错 的场景是:超级表 + 特定窗口/函数合并时间线时。

Q3: Leader 切换时写入会怎样?

短暂失败(Sync leader is unreachable / Sync timeout)。客户端驱动通常会 刷新 Catalog 并重试。应用应实现指数退避重试,避免 thundering herd。

Q4: WAL_LEVEL=0 能用于生产吗?

不能。仅适合纯测试。崩溃必丢数据,且与多副本配置不兼容。

Q5: 如何判断瓶颈在客户端还是服务端?

对比:同机 taosBenchmark 写入 RPS vs 应用 RPS。若 benchmark 高、应用低 → 客户端批量/连接问题;两者都低 → 服务端 WAL/副本/磁盘。

Q6: Schemaless 和 SQL INSERT 故障排查有什么不同?

到达 VNode 之后相同。Schemaless 额外关注:Line 语法错误(Syntax error in Line)、类型冲突(Not the same type like before)、协议/精度参数(Invalid line protocol type)。

参考

系统构架篇

数据模型

存储引擎

查询引擎

数据写入

关于 TDengine

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