分类 :11.客户端与连接器 | 篇章 :05 Go/Rust 连接器
Go 和 Rust 连接器都是 TDengine 高性能场景的常用选择,常用于编写数据采集网关、流处理服务、TaosX 插件。本文统一讲解两者用法。
速查表
| 维度 | Go | Rust |
|---|---|---|
| 包名 | driver-go | taos / taos-ws |
| 接口 | database/sql | sqlx / 异步 |
| Native | taosSql | taos (libtaos) |
| WebSocket | taosWS | taos-ws |
| REST | taosRestful | taos-ws (兼容) |
| 异步 | goroutine | tokio |
Go 连接器详解
1. 安装
bash
go get github.com/taosdata/driver-go/v3
2. 三种 Driver
go
import (
"database/sql"
// Native
_ "github.com/taosdata/driver-go/v3/taosSql"
// WebSocket
_ "github.com/taosdata/driver-go/v3/taosWS"
// REST
_ "github.com/taosdata/driver-go/v3/taosRestful"
)
// 连接字符串
// Native: user:pass@tcp(host:6030)/db
// WebSocket: user:pass@ws(host:6041)/db
// REST: user:pass@http(host:6041)/db
db, err := sql.Open("taosWS",
"root:taosdata@ws(localhost:6041)/test")
3. 基本 CRUD
go
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/taosdata/driver-go/v3/taosWS"
)
func main() {
db, err := sql.Open("taosWS",
"root:taosdata@ws(localhost:6041)/")
if err != nil { log.Fatal(err) }
defer db.Close()
// DDL
_, _ = db.Exec("CREATE DATABASE IF NOT EXISTS test")
_, _ = db.Exec("USE test")
_, _ = db.Exec(`
CREATE STABLE IF NOT EXISTS meters (
ts TIMESTAMP, current FLOAT, voltage INT
) TAGS (location VARCHAR(32))
`)
// INSERT
_, _ = db.Exec(`
INSERT INTO d001 USING meters TAGS('Beijing')
VALUES (NOW, 25.3, 220)
`)
// SELECT
rows, err := db.Query("SELECT * FROM meters LIMIT 10")
if err != nil { log.Fatal(err) }
defer rows.Close()
for rows.Next() {
var ts string
var current float32
var voltage int
var location string
rows.Scan(&ts, ¤t, &voltage, &location)
fmt.Printf("%s %.2f %d %s\n", ts, current, voltage, location)
}
}
4. STMT2 高性能写入
go
import "github.com/taosdata/driver-go/v3/ws/stmt"
config := stmt.NewConfig("ws://localhost:6041/", 0)
config.SetConnectUser("root").SetConnectPass("taosdata")
connector, _ := stmt.NewConnector(config)
stmtH, _ := connector.Init()
stmtH.Prepare("INSERT INTO ? USING meters TAGS(?) VALUES(?, ?, ?)")
// 批量绑定
params := []*stmt.TaosStmt2BindData{...}
stmtH.Bind(params)
stmtH.Exec()
stmtH.Close()
5. TMQ 订阅
go
import (
"github.com/taosdata/driver-go/v3/ws/tmq"
)
config := tmq.NewConfig("ws://localhost:6041/", 0)
config.SetGroupID("my_group")
config.SetAutoOffsetReset("earliest")
config.SetConnectUser("root")
config.SetConnectPass("taosdata")
consumer, _ := tmq.NewConsumer(config)
consumer.Subscribe([]string{"my_topic"})
for {
msg, _ := consumer.Poll(time.Second)
if msg == nil { continue }
// 处理消息
for _, blockInfo := range msg.Data {
// blockInfo.TableName, blockInfo.Data
}
consumer.Commit()
}
consumer.Close()
Rust 连接器详解
6. 依赖
toml
[dependencies]
taos = { version = "0.x", features = ["ws"] }
tokio = { version = "1", features = ["full"] }
7. 基本用法
rust
use taos::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 连接(WebSocket)
let dsn = "taosws://root:taosdata@localhost:6041/test";
let taos = TaosBuilder::from_dsn(dsn)?.build().await?;
// DDL
taos.exec("CREATE DATABASE IF NOT EXISTS test").await?;
taos.exec("USE test").await?;
taos.exec(
"CREATE STABLE IF NOT EXISTS meters \
(ts TIMESTAMP, current FLOAT, voltage INT) \
TAGS (location VARCHAR(32))"
).await?;
// INSERT
taos.exec(
"INSERT INTO d001 USING meters TAGS('Beijing') \
VALUES (NOW, 25.3, 220)"
).await?;
// SELECT
let rows = taos.query("SELECT * FROM meters LIMIT 10").await?;
use futures::TryStreamExt;
let mut stream = rows.rows();
while let Some(row) = stream.try_next().await? {
for (name, value) in row {
println!("{}: {:?}", name, value);
}
}
Ok(())
}
8. STMT2 与 TMQ
rust
// STMT2
use taos::sync::*;
let mut stmt = Stmt2::init(&taos, &Stmt2Option::default())?;
stmt.prepare("INSERT INTO ? USING meters TAGS(?) VALUES(?, ?, ?)")?;
let tbnames = vec!["d001"];
let tags = vec![vec![Value::VarChar("Beijing".into())]];
let cols = vec![vec![
ColumnView::Timestamps(vec![1717488000000].into()),
ColumnView::Floats(vec![25.3].into()),
ColumnView::Ints(vec![220].into()),
]];
stmt.bind(&tbnames, &tags, &cols)?;
stmt.execute()?;
// TMQ
use taos::tmq::*;
let tmq = TmqBuilder::from_dsn(
"taosws://root:taosdata@localhost:6041?group.id=my_group"
)?.build().await?;
tmq.subscribe(["my_topic"]).await?;
while let Some(message) = tmq.recv().await? {
println!("{:?}", message);
tmq.commit(message).await?;
}
代码示例
Go 数据采集服务
go
package main
import (
"database/sql"
"log"
"time"
_ "github.com/taosdata/driver-go/v3/taosWS"
)
type DataPoint struct {
Device string
Ts time.Time
Current float32
Voltage int
}
func main() {
db, _ := sql.Open("taosWS",
"root:taosdata@ws(localhost:6041)/iot")
defer db.Close()
// 准备 schema
setupSchema(db)
// 接收数据并写入
dataChan := make(chan DataPoint, 10000)
go collectData(dataChan)
batch := make([]DataPoint, 0, 1000)
ticker := time.NewTicker(time.Second)
for {
select {
case point := <-dataChan:
batch = append(batch, point)
if len(batch) >= 1000 {
flushBatch(db, batch)
batch = batch[:0]
}
case <-ticker.C:
if len(batch) > 0 {
flushBatch(db, batch)
batch = batch[:0]
}
}
}
}
func flushBatch(db *sql.DB, batch []DataPoint) {
// 用 STMT2 写入
// ...
}
Rust 流处理服务
rust
use taos::*;
use futures::StreamExt;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let dsn = "taosws://root:taosdata@localhost:6041/iot";
let taos = TaosBuilder::from_dsn(dsn)?.build().await?;
// 订阅原始数据
let tmq = TmqBuilder::from_dsn(
"taosws://root:taosdata@localhost:6041?group.id=processor"
)?.build().await?;
tmq.subscribe(["raw_data"]).await?;
while let Some(msg) = tmq.recv().await? {
// 处理数据:聚合、过滤、转换
let processed = process(msg);
// 写入下游表
taos.exec(&format!(
"INSERT INTO processed VALUES ({}, {})",
processed.ts, processed.value
)).await?;
tmq.commit(msg).await?;
}
Ok(())
}
性能考量
Go vs Rust 性能
| 维度 | Go | Rust |
|---|---|---|
| 启动速度 | 快 | 极快 |
| 单线程吞吐 | 高 | 极高 |
| 内存占用 | 中 | 极低 |
| 并发模型 | goroutine | tokio task |
| 学习曲线 | 平缓 | 陡 |
适用场景
| 场景 | 推荐 |
|---|---|
| Web 服务 | Go |
| 数据采集网关 | Go / Rust |
| 极致性能 | Rust |
| TaosX 插件 | Rust |
| K8s Operator | Go |
FAQ
Q1: Go 连接池怎么配?
go
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(time.Hour)
Q2: Rust async 必须用 tokio?
主要支持 tokio。其他 runtime 需自行适配。
Q3: Go 用 GORM 吗?
可以,但 GORM 对时序数据库的高级特性支持有限。推荐直接 database/sql。
Q4: Rust 编译慢怎么办?
- 用 sccache 缓存
- release 模式只在最终构建用
- 拆分 crate 利用增量编译
Q5: 静态链接 libtaos?
Go Native:CGO_ENABLED=1,需 libtaos
Go WebSocket:纯 Go,可静态链接
Rust:features = "ws" 不依赖 libtaos
参考
系统构架篇
- 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 EXPLAIN 与查询优化》
数据写入
- 01-《TDengine SQL INSERT》
- 02-《TDengine 无模式写入》
- 03-《TDengine STMT 写入》
- 04-《TDengine 写入内部流程》
- 05-《TDengine 数据更新删除》
数据订阅
- 01-《TDengine 数据订阅》
- 02-《TDengine 订阅 vs Kafka》
- 03-《TDengine TMQ 消费流程》
- 04-《TDengine 内部机制》
- 05-《TDengine TMQ 最佳实践》
预聚合
索引
SQL 语句
- 01-《TDengine DDL》
- 02-《TDengine DML SELECT》
- 03-《TDengine DML 函数完整参考》
- 04-《TDengine JOIN 完整语法》
- 05-《TDengine 窗口完整语法》
- 06-《TDengine 操作符与表达式》
- 07-《TDengine 系统表》
- 08-《TDengine SQL 与标准 SQL 差异》
客户端与连接器
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。
