TDengine STMT 参数绑定 — 高性能批量写入与查询的最佳方式

分类:5.写入路径 |篇章:03 STMT 参数绑定

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

STMT(Prepared Statement)参数绑定是 TDengine 性能最高的写入方式。SQL 一次预编译、参数多次绑定,避免反复解析;多表交织写入更可单连接达到百万级 RPS。

核心概念速查表

概念说明
Prepare预编译 SQL(一次)
Bind绑定参数(多次)
Execute执行绑定的数据
Multi-Bind一次绑定多行
Interlace多子表交织绑定
STMT1 / STMT2两代 API(STMT2 更优)

详细解析

1. STMT 工作模式

普通 SQL 写入: for each batch: 构造 SQL 字符串 → Parser → Plan → Execute → 返回 [每次都解析 SQL] STMT 写入: Prepare("INSERT INTO ? VALUES (?, ?, ?)") → Parser → Plan → 缓存执行计划 for each batch: Bind(子表名, 时间数组, 值数组) Execute() [复用执行计划,仅传参数] 优势: - SQL 解析仅一次 - 参数二进制传输(无文本编码) - 内存连续布局(列式绑定) - 网络包紧凑

2. STMT 操作序列

典型操作流程: conn = connect(...) stmt = stmt2_init(conn, options) // 1. Prepare 一次 stmt2_prepare(stmt, "INSERT INTO ? USING meters TAGS(?,?) VALUES(?,?,?,?)") // 2. 循环 Bind + Execute loop: stmt2_bind_param(stmt, bindData) stmt2_exec(stmt, &affected_rows) // 3. 释放 stmt2_close(stmt) bindData 结构: - 子表名(自动建表场景) - Tag 值数组 - 列名数组 - 列值数组(按列存储) - 行数

3. 列式绑定

列式绑定示例: 插入 1000 行,4 列(ts, current, voltage, phase): 传统行式: [ts1, c1, v1, p1, ts2, c2, v2, p2, ...] → 内存非连续,缓存不友好 STMT 列式: ts: [ts1, ts2, ..., ts1000] ← 连续 8KB current:[c1, c2, ..., c1000] ← 连续 8KB voltage:[v1, v2, ..., v1000] ← 连续 4KB phase: [p1, p2, ..., p1000] ← 连续 4KB 优势: - SIMD 向量化处理 - 内存连续高速复制 - 服务端直接写入列式存储

4. 多表交织绑定(STMT2 核心)

传统场景:每张子表分别 Prepare/Bind/Execute → 1000 个子表 → 1000 次往返 STMT2 交织绑定: Prepare("INSERT INTO ? USING meters TAGS(?,?) VALUES(?,?,?,?)") 单次 Bind 包含多个子表的数据: sub_tables: [ {tbname: "d001", tags: [...], values: [行1,行2,...]}, {tbname: "d002", tags: [...], values: [行1,行2,...]}, ... {tbname: "d100", tags: [...], values: [行1,行2,...]}, ] Execute() → 一次执行写入 100 个子表 1000 行 → 服务端按 VGroup 拆分并行处理

5. NULL 与空值处理

NULL 的表示: 每列有 is_null 数组(同长度): values: [25.3, 25.5, NULL, 25.7] is_null: [0, 0, 1, 0] Bind 时: is_null[i] = 1 → 表示第 i 行该列为 NULL is_null[i] = 0 → 使用 values[i] 字符串/二进制: 额外有 length 数组(指定每行字符串长度) values 是变长拼接

6. STMT 用于查询

参数化查询: Prepare("SELECT * FROM meters WHERE location=? AND ts > ?") Bind: ts column: [...] -- 不适用于查询 实际:bind 单值参数 Execute → 返回结果集 查询用 STMT 的好处: - 防 SQL 注入 - 避免重复 Parser - 应用层模板化 查询参数绑定较少用,因为查询通常多变;写入参数绑定收益最大。

7. 错误处理

常见错误: 错误码 / 含义: - 0x80002659: Parameter 数量不匹配 - 0x80002650: 列类型不匹配 - 0x80002653: Bind 数据为空 - 0x80000404: 子表 USING 时 TAGS 不全 错误恢复策略: - Bind/Execute 失败 → 检查参数 - 网络错误 → 重新 Prepare 后重试 - 子表不存在 → 用 USING 自动建表

8. STMT2 vs STMT1

特性STMT1STMT2
单表 Bind
多表交织
自动建表 USING✓(更高效)
性能极高
推荐兼容旧版新项目首选

代码示例

Python STMT2 写入

importtaosimportdatetime conn=taos.connect(...)stmt=conn.statement2("INSERT INTO ? USING meters TAGS(?,?) VALUES(?,?,?,?)")# 准备数据sub_tables=['d001','d002']tags=[['Beijing',2],['Shanghai',3]]data=[[# d001[int(datetime.datetime.now().timestamp()*1000)+iforiinrange(100)],[25.0+i*0.01foriinrange(100)],[220]*100,[0.5]*100,],[# d002[int(datetime.datetime.now().timestamp()*1000)+iforiinrange(100)],[26.0+i*0.01foriinrange(100)],[221]*100,[0.6]*100,],]stmt.bind_param(sub_tables,tags,data)stmt.execute()print(f"写入{stmt.affected_rows}行")

Java STMT 示例

Stringsql="INSERT INTO ? USING meters TAGS(?,?) VALUES(?,?,?,?)";try(TSWSPreparedStatementpstmt=conn.prepareStatement(sql).unwrap(TSWSPreparedStatement.class)){pstmt.setTableName("d001");pstmt.setTagString(0,"Beijing");pstmt.setTagInt(1,2);pstmt.setTimestamp(0,timestamps);pstmt.setFloat(1,currents);pstmt.setInt(2,voltages);pstmt.setFloat(3,phases);pstmt.columnDataAddBatch();pstmt.columnDataExecuteBatch();}

性能考量

写入性能阶梯

方式单连接 RPS备注
单行 SQL INSERT~5K极慢
批量 SQL INSERT (1000 行/批)~800K通用
Schemaless~600K自动建表
STMT1 单表~1M高效
STMT2 交织(100 子表)~3M+极致

调优要点

建议
单批行数1000~10000
单批子表数50~500
并发连接数CPU 核数
服务端配置增加 mqStream/numOfRpc

FAQ

Q1: STMT 比 Schemaless 快多少?

通常 3~5 倍。Schemaless 需文本解析+Schema 比对;STMT 二进制+列式+缓存计划。

Q2: STMT 能动态改 SQL 吗?

不能。SQL 在 Prepare 时固定。改 SQL 需要重新 Prepare。

Q3: STMT 写入失败如何重试?

捕获错误后可:

  • 直接 Re-Execute(同样数据)
  • 也可重 Bind 后 Execute
  • 若 Prepare 失效需重新 Prepare

Q4: 多线程能共享同一个 STMT 吗?

不建议。每个线程独立 conn + stmt 最稳定。共享会有锁竞争。

Q5: TAGS 能省略吗?

USING 子句必须提供 TAGS。已存在的子表可省略 USING 直接 INSERT INTO 子表。

参考

系统构架篇

  • 01-《TDengine 整体架构全景》
  • 02-《集群拓扑深度解析》
  • 03-《MNode 内部机制深度解析》
  • 04-《RPC 通信层深度解析》
  • 05-《VNode 生命周期》
  • 06-《RAFT 共识协议》
  • 07-《端到端的消息流》

数据模型

  • 01-《数据库创建与参数详解》
  • 02-《超级表/子表/普通表》
  • 03-《支持数据类型深度解析》
  • 04-《TDengine Tag 设计哲学与 Schema 变更机制》
  • 05-《TDengine 虚拟表实现原理》

存储引擎

  • 01-《TDengine 存储引擎概览》
  • 02-《TDengine MemTable 深度解析》
  • 03-《TDengine WAL 预写日志机制》
  • 04-《TDengine 数据文件格式》
  • 05-《TDengine Commit 与 Flush 机制 》
  • 06-《TDengine Compaction 合并策略 》
  • 07-《TDengine 数据保留与 TTL》
  • 08-《TDengine 压缩编码机制》
  • 09-《TDengine Cache 与 Last 查询加速》
  • 10-《TDengine 逻辑计划生成》

查询引擎

  • 01-《TDengine 查询引擎概览》
  • 02-《TDengine SQL 解析与词法分析》
  • 03-《TDengine 语义分析与 AST 重写》
  • 04-《TDengine 逻辑计划生成》
  • 05-《TDengine 物理计划生成》
  • 06-《TDengine 扫描算子》
  • 07-《TDengine 聚合算子》
  • 08-《TDengine 聚合算子》
  • 09-《TDengine 连接算子》
  • 10-《TDengine 排序、填充与投影》
  • 11-《TDengine 分布式查询执行》
  • 12-《TDengine EXPLAIN 与查询优化》

数据写入

  • 01-《TDengine SQL INSERT》

关于 TDengine

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