
TDengine Python 连接器进阶指南:性能、并发与工程化实践
面向已能完成"连接/建库建表/写入/查询"的开发者。本指南基于官方 Python 连接器
taospy(原生taos)与taos-ws-py(WebSockettaosws)现有能力,聚焦更高吞吐、更稳定、更易排障的用法。
1. 先选对连接方式:原生 taos vs WebSocket taosws
1.1 能力与依赖差异(工程视角)
- 原生连接(
taospy的taos模块)- 依赖客户端驱动(
libtaos.so/taos.dll) - 适合:本机/同网段部署、依赖可控、追求极致单连接性能
- 支持:写入、查询、TMQ 订阅、schemaless、参数绑定(含 STMT2)等
- 依赖客户端驱动(
- WebSocket 连接(
taos-ws-py的taosws模块,推荐)- 通过
taosAdapter的 WebSocket 接口连接,依赖更轻 - 适合:容器化/跨平台/易部署、多线程 IO 场景、需要连接负载均衡、多地址容灾
- 文档强调:在 IO 密集并发下更容易绕开 Python GIL 带来的性能瓶颈(数据解析等由 WebSocket 连接器与 taosAdapter 承担)
- 通过
1.2 典型选型建议
- 写入吞吐优先 + 多线程并发 :优先选 WebSocket(
taosws) - 同机部署 + 强依赖 C 驱动能力/已有 native 生态 :选原生(
taos) - 要接入 SQLAlchemy / pandas :两者都可,但
taosws更常用于连接池与并发(示例也更丰富)
2. 连接与 DSN:把"可运维参数"显式化
2.1 WebSocket:DSN 与关键参数
官方文档给出了 URL 规范与参数(建议优先用这些参数做"可观测与稳定性"配置):
timezone:IANA 时区(如Asia/Shanghai)compression:是否压缩(true/false)conn_retries/retry_backoff_ms/retry_backoff_max_ms:连接重试与退避read_timeout:响应超时(秒,不含订阅)
场景:长查询/大结果集超时
python
import taosws
conn = taosws.connect(
user="root",
password="taosdata",
host="localhost",
port=6041,
timezone="Asia/Shanghai",
read_timeout="600", # 例如拉长到 10 分钟
conn_retries="10",
retry_backoff_ms="200",
retry_backoff_max_ms="2000",
)
场景:用 DSN 快速连接(示例用法)
python
import taosws
conn = taosws.connect("taosws://root:taosdata@localhost:6041")
场景:多地址(负载均衡/容灾)与时区(SQLAlchemy URL 示例)
python
from sqlalchemy import create_engine
engine = create_engine(
url="taosws://root:taosdata@?hosts=localhost:6041,127.0.0.1:6041&timezone=Asia/Shanghai",
pool_size=10,
max_overflow=20,
)
2.2 安全认证(云/企业环境常见)
WebSocket 连接参数支持:
token:云服务 tokentotp_code:TOTP 一次性密码bearer_token:令牌认证
场景:生产环境使用令牌认证(示意)
python
import taosws
conn = taosws.connect(
host="your-host",
port=6041,
user="root",
password="***",
bearer_token="***", # 或 totp_code="123456" / token="***"
)
建议:将这些参数统一从环境变量或配置中心注入,避免硬编码在代码里。
3. 请求追踪:用 req_id 把一条 SQL 全链路串起来
连接器支持在 execute()/query() 时携带请求 ID(示例中使用 req_id=),用于问题定位与日志关联。
场景:线上排障,需要从应用日志定位到 TDengine 端
python
import taosws
conn = taosws.connect("taosws://root:taosdata@localhost:6041")
conn.execute("create database if not exists connwspy keep 36500", req_id=2)
conn.execute("use connwspy", req_id=3)
result = conn.query("select * from stb", req_id=10)
for row in result:
print(row)
实践建议:
- 每个入口请求(HTTP/RPC)生成一个
req_id(或从上游透传) - 对同一业务请求内的多条 SQL 复用同一个
req_id - 异常日志中同时打印
req_id + SQL + 参数概要(注意脱敏)
4. 高性能写入三件套:批量 SQL、STMT、STMT2
4.1 批量 SQL(最简单,但要注意 SQL 拼接成本)
适合:快速验证、低到中等吞吐、SQL 可控场景。
python
sql = """
INSERT INTO
power.d1001 USING power.meters TAGS('California.SanFrancisco', 2)
VALUES (NOW + 1a, 10.3, 219, 0.31)
(NOW + 2a, 12.6, 218, 0.33)
power.d1002 USING power.meters TAGS('California.SanFrancisco', 3)
VALUES (NOW + 1a, 10.3, 218, 0.25)
"""
affected = conn.execute(sql)
建议:
- 单条 INSERT 内合并多行写入,减少往返
- 控制单条 SQL 长度,避免过大导致解析/传输成本上升
- 大吞吐写入优先考虑 STMT/STMT2
4.2 STMT(参数绑定,批量绑定多行)
适合:结构固定、需要显著减少拼接与类型转换开销、写入频繁。
场景:多子表批量写入(绑定多行)
python
import taos
from datetime import datetime
def get_ts(ts: str):
dt = datetime.strptime(ts, "%Y-%m-%d %H:%M:%S.%f")
return int(dt.timestamp() * 1000)
conn = taos.connect(database="power")
stmt = conn.statement("INSERT INTO ? USING meters TAGS(?, ?) VALUES(?, ?, ?, ?)")
tag_params = taos.new_bind_params(2)
tag_params[0].binary("California.SanFrancisco")
tag_params[1].int(2)
stmt.set_tbname_tags("d1001", tag_params)
value_params = taos.new_multi_binds(4)
value_params[0].timestamp([get_ts("2018-10-03 14:38:05.000")])
value_params[1].float([10.3])
value_params[2].int([219])
value_params[3].float([0.31])
stmt.bind_param_batch(value_params)
stmt.execute()
stmt.close()
conn.close()
4.3 STMT2(更推荐的高效绑定接口:原生与 WebSocket 都支持)
适合:大量子表、多列、多行持续写入;追求吞吐与稳定的首选。
WebSocket(taosws)STMT2:列式绑定 + 一次绑定多表
python
from datetime import datetime
import random
import taosws
conn = taosws.connect(user="root", password="taosdata", host="localhost", port=6041)
conn.execute("CREATE DATABASE IF NOT EXISTS power")
conn.execute("USE power")
conn.execute(
"CREATE TABLE IF NOT EXISTS meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) "
"TAGS (groupid INT, location BINARY(16))"
)
sql = "INSERT INTO ? USING meters (groupid, location) TAGS(?,?) VALUES (?,?,?,?)"
stmt2 = conn.stmt2_statement()
stmt2.prepare(sql)
current = int(datetime.now().timestamp() * 1000)
timestamps = [current + i for i in range(10)]
currents = [random.random() * 30 for _ in range(10)]
voltages = [random.randint(100, 300) for _ in range(10)]
phases = [random.random() for _ in range(10)]
param = taosws.stmt2_bind_param_view(
table_name="d_bind_0",
tags=[taosws.int_to_tag(0), taosws.varchar_to_tag("location_0")],
columns=[
taosws.millis_timestamps_to_column(timestamps),
taosws.floats_to_column(currents),
taosws.ints_to_column(voltages),
taosws.floats_to_column(phases),
],
)
stmt2.bind([param])
rows = stmt2.execute()
stmt2.close()
conn.close()
原生(taos)STMT2:按"表数组"组织数据(更贴近业务批处理)
python
import taos
from datetime import datetime
import random
conn = taos.connect(user="root", password="taosdata", host="localhost", port=6030)
conn.execute("CREATE DATABASE IF NOT EXISTS power")
conn.execute("USE power")
conn.execute(
"CREATE TABLE IF NOT EXISTS meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) "
"TAGS (groupid INT, location BINARY(16))"
)
sql = "INSERT INTO ? USING meters (groupid, location) TAGS(?,?) VALUES (?,?,?,?)"
stmt2 = conn.statement2(sql)
tbnames, tags, datas = [], [], []
for i in range(10):
tbnames.append(f"d_bind_{i}")
tags.append([i, f"location_{i}"])
base = int(datetime.now().timestamp() * 1000)
timestamps = [base + j for j in range(10)]
currents = [float(random.random() * 30) for _ in range(10)]
voltages = [random.randint(100, 300) for _ in range(10)]
phases = [float(random.random()) for _ in range(10)]
datas.append([timestamps, currents, voltages, phases])
stmt2.bind_param(tbnames, tags, datas)
stmt2.execute()
stmt2.close()
conn.close()
选型建议(写入侧)
- 低频/少量:批量 SQL 即可
- 中高频:STMT
- 高吞吐/多子表:优先 STMT2(尤其是 WebSocket 场景)
5. Schemaless 写入:协议、精度、TTL 的组合拳
连接器支持 schemaless 三种协议:
- InfluxDB Line Protocol
- OpenTSDB Telnet line
- JSON protocol
WebSocket 示例(带 precision、ttl、req_id):
python
import taosws
conn = taosws.connect(user="root", password="taosdata", host="localhost", port=6041, database="power")
lineDemo = [
"meters,groupid=2,location=California.SanFrancisco current=10.3000002f64,voltage=219i32,phase=0.31f64 1626006833639"
]
conn.schemaless_insert(
lines=lineDemo,
protocol=taosws.PySchemalessProtocol.Line,
precision=taosws.PySchemalessPrecision.Millisecond,
ttl=1,
req_id=1,
)
conn.close()
实践建议:
- 明确
precision,避免时间戳单位误判造成"数据跑偏" ttl(天)用于控制表生命周期,适合临时/边缘数据- 批量提交
lines,减少调用次数
6. 查询与结果集:迭代、字段信息与大结果集策略
6.1 WebSocket 结果集迭代(示例)
python
result = conn.query("select * from meters")
print(result.field_count)
for row in result:
print(row)
6.2 大结果集常见策略
- 优先在 SQL 侧做过滤与聚合:避免把"无意义的海量数据"搬到 Python
- 合理设置
read_timeout:长查询避免误判超时 - 批处理消费:如果业务允许,按时间窗口/分片查询(例如按天/小时)
6.3 纳秒时间戳注意事项
文档说明:Python 对纳秒支持不完善,连接器在纳秒精度下可能返回整数 而非 datetime。建议使用 pandas 转换:
python
import pandas as pd
ns_value = 1700000000000000000 # 示例纳秒时间戳
dt = pd.to_datetime(ns_value, unit="ns")
7. 并发与连接池:线程不要共享连接,池化才是正解
7.1 SQLAlchemy 连接池(WebSocket 示例)
适合:应用已有 SQLAlchemy 生态;希望统一连接池、事务上下文(注意 TDengine 的语义以实际支持为准)、多线程并发。
python
from sqlalchemy import create_engine, text
engine = create_engine(
url="taosws://root:taosdata@?hosts=localhost:6041,127.0.0.1:6041&timezone=Asia/Shanghai",
pool_size=10,
max_overflow=20,
)
def ws_query(sql: str):
with engine.begin() as conn:
result = conn.execute(text(sql))
return result.fetchall()
7.2 DBUtils 连接池(示例:PooledDB)
适合:不想引入 SQLAlchemy、但需要稳定的连接池;也适合"线程池 + DB 连接池"的简单架构。
python
from dbutils.pooled_db import PooledDB
import taosws
websocket_pool = PooledDB(
creator=taosws,
maxconnections=10,
host="localhost",
port=6041,
user="root",
password="taosdata",
charset="UTF-8",
)
def ws_query(sql: str):
conn = None
cursor = None
try:
conn = websocket_pool.connection()
cursor = conn.cursor()
cursor.execute(sql)
return cursor.fetchall()
finally:
if cursor:
cursor.close()
if conn:
conn.close() # 归还给连接池
7.3 并发实践要点
- 不要跨线程共享同一个
conn/cursor - 每个线程/任务从池里取连接,用完归还
- IO 密集任务可用多线程;CPU 密集(大量数据转换)建议用多进程/分布式
8. TMQ 订阅(WebSocket 示例):消费、位点与回溯
订阅相关常用配置(文档列出):
group.id/client.idauto.offset.reset:latest/earliestenable.auto.commit/auto.commit.interval.ms- 连接参数(host/port/user/pass/token 等)
场景:创建 topic + 消费 + 手动提交位点
python
import taosws
# 1) 先创建 topic(示例)
conn = taosws.connect(user="root", password="taosdata", host="localhost", port="6041")
conn.execute("CREATE DATABASE IF NOT EXISTS power")
conn.execute("USE power")
conn.execute("CREATE TOPIC IF NOT EXISTS topic_meters AS SELECT ts, current FROM meters")
conn.close()
# 2) 创建消费者并订阅
consumer = taosws.Consumer(conf={
"td.connect.websocket.scheme": "ws",
"group.id": "group1",
"client.id": "1",
"auto.offset.reset": "latest",
"td.connect.ip": "localhost",
"td.connect.port": "6041",
"enable.auto.commit": "false",
})
consumer.subscribe(["topic_meters"])
records = consumer.poll(timeout=1.0)
if records:
for block in records:
for row in block:
print(row)
consumer.commit(records) # 处理完成后提交
consumer.unsubscribe()
consumer.close()
场景:从最早位点回放(用于重放/补数)
- 配置
auto.offset.reset=earliest - 或使用
assignment()+seek(topic, vg_id, offset)将分区 offset 指到begin()
9. 异常处理与可观测性:把错误分层,并保留 errno/msg
连接器异常类型(文档给出)包括 ConnectionError、ProgrammingError、QueryError、StatementError、SchemalessError、TmqError 等。
场景:捕获并打印关键字段(原生示例)
python
import taos
try:
conn = taos.connect()
conn.execute("CREATE TABLE 123") # 错误 SQL
except taos.Error as e:
print("exception class:", e.__class__.__name__)
print("error number:", e.errno)
print("error message:", e.msg)
工程化建议:
- 统一捕获入口:记录
req_id、SQL(或 SQL 模板)、关键参数摘要、errno/msg - 对"可重试错误"(临时网络问题)做有限重试;对"语法/类型不匹配"直接失败
- 订阅消费端:务必处理异常并确保
close()被调用,避免资源泄漏
10. 进阶组合方案(按场景给出"推荐实现")
场景 A:高吞吐写入(多子表)+ 可横向扩展
- WebSocket
taosws+ STMT2 - 多进程生产数据(或多线程 IO)+ 连接池(DBUtils/SQLAlchemy)
- 批次大小控制:每批几百到几千行(结合业务与网络情况调优)
场景 B:数据接入协议不统一/需要快速上线
- Schemaless(Line/Telnet/JSON)批量提交
- 明确
precision+ 设置ttl(临时数据/边缘数据很有用) - 结合
req_id做全链路追踪
场景 C:实时消费(告警/ETL)+ 可回溯
- TMQ Consumer
- 关闭 auto commit,业务处理完成后手动
commit - 通过
seek回放历史区间
附:快速自检清单(上线前)
- 连接参数显式设置:
timezone、read_timeout、conn_retries、退避参数 - 写入方式选型正确:吞吐上来后优先 STMT2
- 并发模型正确:不共享连接;使用连接池
- 观测完善:日志里带
req_id、错误errno/msg、必要的 SQL 上下文 - 纳秒时间戳处理明确:必要时转 pandas
参考资料
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。