TDengine Python 连接器进阶指南

TDengine Python 连接器进阶指南:性能、并发与工程化实践

面向已能完成"连接/建库建表/写入/查询"的开发者。本指南基于官方 Python 连接器 taospy(原生 taos)与 taos-ws-py(WebSocket taosws)现有能力,聚焦更高吞吐、更稳定、更易排障的用法。


1. 先选对连接方式:原生 taos vs WebSocket taosws

1.1 能力与依赖差异(工程视角)

  • 原生连接(taospytaos 模块)
    • 依赖客户端驱动(libtaos.so/taos.dll
    • 适合:本机/同网段部署、依赖可控、追求极致单连接性能
    • 支持:写入、查询、TMQ 订阅、schemaless、参数绑定(含 STMT2)等
  • WebSocket 连接(taos-ws-pytaosws 模块,推荐)
    • 通过 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:云服务 token
  • totp_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 示例(带 precisionttlreq_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.id
  • auto.offset.resetlatest / earliest
  • enable.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

连接器异常类型(文档给出)包括 ConnectionErrorProgrammingErrorQueryErrorStatementErrorSchemalessErrorTmqError 等。

场景:捕获并打印关键字段(原生示例)

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 回放历史区间

附:快速自检清单(上线前)

  • 连接参数显式设置:timezoneread_timeoutconn_retries、退避参数
  • 写入方式选型正确:吞吐上来后优先 STMT2
  • 并发模型正确:不共享连接;使用连接池
  • 观测完善:日志里带 req_id、错误 errno/msg、必要的 SQL 上下文
  • 纳秒时间戳处理明确:必要时转 pandas

参考资料

  1. Python 连接器接口大全
  2. Python 连接器开源地址
  3. Python 连接器下载(原生)
  4. Python 连接器下载(WebSocket)

关于 TDengine

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

相关推荐
赵渝强老师2 小时前
【赵渝强老师】OceanBase的配置文件与配置项
数据库·oceanbase
brent4232 小时前
DAY50复习日
开发语言·python
万行3 小时前
机器学习&第三章
人工智能·python·机器学习·数学建模·概率论
Data_agent3 小时前
Cocbuy 模式淘宝 / 1688 代购系统(欧美市场)搭建指南
开发语言·python
玖日大大3 小时前
OceanBase SeekDB:AI 原生数据库的技术革命与实践指南
数据库·人工智能·oceanbase
m0_726365833 小时前
哈希分分预测系统 打造自适应趋势分析「Python+DeepSeek+PyQt5」
python·qt·哈希算法
vyuvyucd3 小时前
Qwen-1.8B-Chat昇腾Atlas800TA2部署实战
python
轻竹办公PPT3 小时前
2026 年工作计划 PPT 内容拆解,对比不同 AI 生成思路
人工智能·python·powerpoint
癫狂的兔子3 小时前
【Python】【Flask】抽奖功能
开发语言·python·flask