在这里插入图片描述

TDengine REST API 使用手册

目录


概述

TDengine 提供符合 RESTful 设计标准的 HTTP API,通过简单的 HTTP 请求即可操作数据库。为降低学习成本,TDengine 采用独特的设计:直接在 HTTP POST 请求体中包含 SQL 语句来操作数据库,仅需一个 URL 即可完成所有操作。

适用场景

REST API 特别适合以下应用场景:

场景说明典型应用
跨平台开发适用于任何支持 HTTP 协议的编程语言和平台嵌入式系统、移动应用、Web 应用
轻量级客户端无需安装 TDengine 客户端库,降低部署复杂度IoT 设备、边缘计算节点
微服务架构与容器化、微服务架构完美契合Docker、Kubernetes 环境
快速原型开发使用 curl、Postman 等工具即可快速验证POC、Demo、调试
云原生应用特别适合云环境和 Serverless 架构AWS Lambda、Azure Functions
防火墙友好HTTP/HTTPS 协议通常不会被防火墙拦截跨网络边界的数据访问
异构系统集成作为统一的数据访问接口多语言、多系统集成

REST API 的优势

1. 零依赖部署
  • ✅ 客户端无需安装任何 TDengine 库或驱动
  • ✅ 只需标准的 HTTP 客户端即可
  • ✅ 减少依赖冲突和版本兼容性问题
2. 广泛的语言支持
  • ✅ Python、Java、Go、Node.js、C#、PHP、Ruby 等所有主流语言
  • ✅ 甚至支持 Shell、JavaScript(浏览器)
  • ✅ 统一的接口,无需学习不同语言的专用驱动
3. 易于调试和测试
  • ✅ 使用 curl、Postman、HTTPie 等通用工具
  • ✅ 可以直接在浏览器中测试(使用插件)
  • ✅ 方便日志记录和问题排查
4. 架构灵活性
  • ✅ 可方便地通过 Nginx、HAProxy 实现负载均衡
  • ✅ 支持 API Gateway 统一管理
  • ✅ 易于实现高可用架构
5. 安全性
  • ✅ 支持 HTTPS 加密传输
  • ✅ 多种认证方式:Basic、Token、自定义
  • ✅ 可与企业现有的认证系统集成
6. 标准化
  • ✅ 遵循 RESTful 设计规范
  • ✅ 统一的错误码和响应格式
  • ✅ 学习成本低,文档友好

与原生连接器的差异

⚠️ 重要提示

REST API 是无状态接口,每次请求都是独立的,这意味着:

特性原生连接器REST API
状态保持✅ 有状态(支持会话)❌ 无状态(每次独立)
USE 命令✅ 生效❌ 不生效
表名前缀可选必须指定数据库名前缀
性能更高(直接连接)略低(HTTP 开销)
安装要求需要安装客户端库无需安装
连接池内置支持需自行实现或使用 HTTP 连接池

解决方案:

  • 方案 1:在 SQL 中使用完整表名,如 demo.meters
  • 方案 2:在 URL 中指定数据库名,如 http://host:6041/rest/sql/demo

环境准备

安装要求

客户端:

  • ✅ 无需安装任何 TDengine 组件
  • ✅ 只需要支持 HTTP 协议的编程语言或工具

服务端:

  • ✅ TDengine Server 2.x 或 3.x
  • ✅ taosAdapter 服务(提供 REST API)

服务端配置

1. 确认 TDengine Server 运行状态
# 检查 TDengine Server
systemctl status taosd

# 如未启动,执行启动命令
systemctl start taosd
2. 启动 taosAdapter 服务
# 启动 taosAdapter
systemctl start taosadapter

# 查看服务状态
systemctl status taosadapter

# 设置开机自启
systemctl enable taosadapter
3. 确认端口监听
# 检查 taosAdapter 是否监听 6041 端口(默认)
netstat -tuln | grep 6041
# 或
ss -tuln | grep 6041
4. 配置调整(可选)

编辑配置文件 /etc/taos/taosadapter.toml:

[http]
# REST API 监听端口,默认 6041
port = 6041

# 是否启用 HTTPS
enable = true

# SSL 证书配置(启用 HTTPS 时需要)
# certFile = "/path/to/cert.pem"
# keyFile = "/path/to/key.pem"

[log]
# 日志级别:trace, debug, info, warn, error, panic
level = "info"

修改配置后需要重启服务:

systemctl restart taosadapter

快速入门

连接验证

使用 curl 命令验证 REST API 是否正常工作:

curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "SELECT name, ntables, status FROM information_schema.ins_databases;" \
  http://localhost:6041/rest/sql

参数说明:

  • -L:跟随 HTTP 重定向
  • -H:设置 HTTP 请求头(这里是认证信息)
  • -d:POST 请求体(SQL 语句)
  • cm9vdDp0YW9zZGF0YQ==:root:taosdata 的 Base64 编码

成功响应示例:

{
    "code": 0,
    "column_meta": [
        ["name", "VARCHAR", 64],
        ["ntables", "BIGINT", 8],
        ["status", "VARCHAR", 10]
    ],
    "data": [
        ["information_schema", 16, "ready"],
        ["performance_schema", 9, "ready"]
    ],
    "rows": 2
}

响应字段说明:

  • code:返回码,0 表示成功
  • column_meta:列元数据(列名、类型、长度)
  • data:查询结果数据
  • rows:返回的行数

基本操作示例

1. 创建数据库
curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "CREATE DATABASE IF NOT EXISTS demo KEEP 365 DURATION 10 BUFFER 16" \
  http://localhost:6041/rest/sql

响应:

{
    "code": 0,
    "column_meta": [["affected_rows", "INT", 4]],
    "data": [[0]],
    "rows": 1
}
2. 创建超级表(Super Table)
curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "CREATE STABLE IF NOT EXISTS demo.meters (
        ts TIMESTAMP, 
        current FLOAT, 
        voltage INT, 
        phase FLOAT
      ) TAGS (
        location BINARY(64), 
        groupId INT
      )" \
  http://localhost:6041/rest/sql
3. 创建子表并插入数据

单条插入:

curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "INSERT INTO demo.d1001 USING demo.meters TAGS ('California.SanFrancisco', 2) 
      VALUES (NOW, 10.3, 219, 0.31)" \
  http://localhost:6041/rest/sql

批量插入:

curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "INSERT INTO 
        demo.d1001 VALUES 
          ('2024-01-22 10:00:00', 10.2, 220, 0.30)
          ('2024-01-22 10:00:01', 10.3, 219, 0.31)
          ('2024-01-22 10:00:02', 10.4, 221, 0.32)
        demo.d1002 USING demo.meters TAGS ('California.LosAngeles', 2) VALUES
          ('2024-01-22 10:00:00', 11.2, 218, 0.28)" \
  http://localhost:6041/rest/sql
4. 查询数据

基础查询:

curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "SELECT * FROM demo.meters WHERE ts >= NOW - 1h LIMIT 100" \
  http://localhost:6041/rest/sql

聚合查询:

curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "SELECT 
        FIRST(ts), 
        AVG(current), 
        MAX(voltage), 
        MIN(phase) 
      FROM demo.meters 
      WHERE ts >= NOW - 1d 
      INTERVAL(1h) 
      GROUP BY location" \
  http://localhost:6041/rest/sql
5. 使用 URL 指定数据库

当在 URL 中指定数据库名后,SQL 语句中可以省略数据库前缀:

# URL 中指定了 demo 数据库
curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "SELECT * FROM meters WHERE ts >= NOW - 1h" \
  http://localhost:6041/rest/sql/demo

API 详细说明

HTTP 请求格式

URL 格式
http://<fqdn>:<port>/rest/sql/[db_name][?tz=timezone][&req_id=req_id][&row_with_meta=true]

参数详解:

参数必选说明示例
fqdn✅TDengine Server 的 FQDN 或 IP 地址192.168.1.100
port✅taosAdapter 监听端口6041(默认)
db_name❌默认数据库名,SQL 中未指定数据库时使用demo
tz❌返回时间的时区,遵循 IANA 规则America/New_York
Asia/Shanghai
req_id❌请求 ID,用于请求追踪和日志关联req_20240122_001
row_with_meta❌是否每行数据携带列名(3.3.2.0+)true 或 false

URL 示例:

# 基础 URL
http://192.168.1.100:6041/rest/sql

# 指定数据库
http://192.168.1.100:6041/rest/sql/demo

# 指定时区
http://192.168.1.100:6041/rest/sql?tz=Asia/Shanghai

# 完整参数
http://192.168.1.100:6041/rest/sql/demo?tz=Asia/Shanghai&req_id=req001&row_with_meta=true
HTTP 方法
  • POST:所有 SQL 操作都使用 POST 方法
请求头(Headers)

必需的请求头:

Content-Type: text/plain
Authorization: <认证类型> <TOKEN>

可选的请求头:

Accept: application/json
Accept-Encoding: gzip, deflate
请求体(Body)

请求体包含完整的 SQL 语句:

SELECT * FROM demo.meters WHERE ts >= NOW - 1h;

注意事项:

  • ✅ 支持单条或多条 SQL 语句(分号分隔)
  • ✅ 支持复杂的 SQL 语法(JOIN、子查询等)
  • ❌ 不要在 SQL 中使用 USE database 语句(无效)

认证机制

TDengine REST API 支持三种认证方式:

1. Basic 认证(推荐用于测试)

格式:

Authorization: Basic <TOKEN>

TOKEN 生成:

# TOKEN = Base64(username:password)
echo -n "root:taosdata" | base64
# 输出:cm9vdDp0YW9zZGF0YQ==

使用示例:

curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

或使用 -u 参数:

curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql
2. Token 认证(推荐用于生产环境,3.4.0+)

优势:

  • ✅ 更安全(由服务器生成和管理)
  • ✅ 支持过期时间
  • ✅ 可以撤销

创建 Token:

-- 使用 taos CLI 或其他客户端创建
CREATE TOKEN myapp_token FROM USER root;

返回示例:

token: bjUvkeBfqFsrXBSj8QjnORJcN0nyA6vdkLNAbkI2MhbWPt289OnIQcZHDIDa8SR

使用 Token:

curl -L -H "Authorization: Bearer bjUvkeBfqFsrXBSj8QjnORJcN0nyA6vdkLNAbkI2MhbWPt289OnIQcZHDIDa8SR" \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

Token 管理:

-- 查看 Token 列表
SHOW TOKENS;

-- 撤销 Token
DROP TOKEN myapp_token;
3. 自定义授权码

获取授权码:

curl http://localhost:6041/rest/login/root/taosdata

响应:

{
  "code": 0,
  "desc": "/KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04"
}

使用授权码:

curl -L -H "Authorization: Taosd /KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04" \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

HTTP 响应格式

HTTP 状态码

TDengine 3.0.3.0+ 提供 httpCodeServerError 配置参数控制错误时的 HTTP 状态码。

当 httpCodeServerError = false(默认):

情况HTTP 状态码
C 接口调用成功200
C 接口调用出错(非鉴权)200
HTTP 请求 URL 参数错误400
C 接口调用鉴权错误401
接口不存在404
系统资源不足503

当 httpCodeServerError = true:

情况HTTP 状态码
C 接口调用成功200
URL 参数错误和参数解析错误400
C 接口调用鉴权错误401
接口不存在404
C 接口调用网络错误502
系统资源不足503
其他 C 接口调用错误500
响应体格式

成功执行插入操作:

{
  "code": 0,
  "column_meta": [["affected_rows", "INT", 4]],
  "data": [[5]],
  "rows": 1
}

成功执行查询操作:

{
  "code": 0,
  "column_meta": [
    ["ts", "TIMESTAMP", 8],
    ["current", "FLOAT", 4],
    ["voltage", "INT", 4],
    ["phase", "FLOAT", 4]
  ],
  "data": [
    ["2024-01-22T10:00:00.000Z", 10.3, 219, 0.31],
    ["2024-01-22T10:00:01.000Z", 10.4, 220, 0.32]
  ],
  "rows": 2
}

执行错误:

{
  "code": 9728,
  "desc": "syntax error near \"FORM\""
}

响应字段说明:

字段类型说明
codeint返回码,0 表示成功,非 0 表示错误
column_metaarray列元数据,格式:[列名, 类型, 长度]
dataarray查询结果数据,二维数组
rowsint返回的行数
descstring错误描述(仅错误时)
数据类型对照表
TDengine 类型REST API 字符串JSON 数据类型说明
TIMESTAMP“TIMESTAMP”stringRFC3339 格式
BOOL“BOOL”booleantrue/false
TINYINT“TINYINT”number-128~127
SMALLINT“SMALLINT”number-32768~32767
INT“INT”number-2³¹~2³¹-1
BIGINT“BIGINT”number-2⁶³~2⁶³-1
FLOAT“FLOAT”number单精度浮点
DOUBLE“DOUBLE”number双精度浮点
VARCHAR“VARCHAR”string变长字符串
NCHAR“NCHAR”stringUnicode 字符串
JSON“JSON”objectJSON 对象
VARBINARY“VARBINARY”stringHex 字符串
GEOMETRY“GEOMETRY”stringWKB Hex 字符串
BLOB“BLOB”stringHex 字符串
DECIMAL“DECIMAL(M,N)”string定点数

特殊类型示例:

GEOMETRY 类型:

# 准备数据
curl -L -u root:taosdata \
  -d "CREATE DATABASE IF NOT EXISTS demo;
      CREATE TABLE demo.geo_test(ts TIMESTAMP, location GEOMETRY(100));
      INSERT INTO demo.geo_test VALUES(NOW, 'POINT(100 100)');" \
  http://localhost:6041/rest/sql

# 查询
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.geo_test" \
  http://localhost:6041/rest/sql

响应:

{
    "code": 0,
    "column_meta": [
        ["ts", "TIMESTAMP", 8],
        ["location", "GEOMETRY", 100]
    ],
    "data": [
        ["2024-01-22T10:00:00.000Z", "010100000000000000000059400000000000005940"]
    ],
    "rows": 1
}

010100000000000000000059400000000000005940 是 POINT(100 100) 的 WKB (Well-Known Binary) 格式的 Hex 表示


高级功能

时区设置

使用 tz 参数指定返回时间的时区:

# 使用上海时区
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters WHERE ts >= NOW - 1h" \
  "http://localhost:6041/rest/sql?tz=Asia/Shanghai"

# 使用纽约时区
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters WHERE ts >= NOW - 1h" \
  "http://localhost:6041/rest/sql?tz=America/New_York"

支持的时区格式:

  • IANA 时区数据库格式(推荐):Asia/Shanghai、America/New_York
  • UTC 偏移量:UTC、UTC+8、UTC-5

请求追踪

使用 req_id 参数为请求设置唯一标识,便于问题排查:

curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  "http://localhost:6041/rest/sql?req_id=req_20240122_${RANDOM}"

优势:

  • ✅ 在日志中快速定位特定请求
  • ✅ 关联前端请求和后端日志
  • ✅ 构建分布式追踪系统

查看日志:

# taosAdapter 日志位置
tail -f /var/log/taos/taosadapter.log | grep "req_20240122"

Key-Value 格式返回

使用 row_with_meta=true 参数,将数组格式的数据转换为 JSON 对象格式(3.3.2.0+):

标准格式(默认):

curl -L -u root:taosdata \
  -d "SELECT ts, current, voltage FROM demo.d1001 LIMIT 1" \
  http://localhost:6041/rest/sql

响应:

{
    "code": 0,
    "column_meta": [
        ["ts", "TIMESTAMP", 8],
        ["current", "FLOAT", 4],
        ["voltage", "INT", 4]
    ],
    "data": [
        ["2024-01-22T10:00:00.000Z", 10.3, 219]
    ],
    "rows": 1
}

Key-Value 格式:

curl -L -u root:taosdata \
  -d "SELECT ts, current, voltage FROM demo.d1001 LIMIT 1" \
  "http://localhost:6041/rest/sql?row_with_meta=true"

响应:

{
    "code": 0,
    "column_meta": [
        ["ts", "TIMESTAMP", 8],
        ["current", "FLOAT", 4],
        ["voltage", "INT", 4]
    ],
    "data": [
        {
            "ts": "2024-01-22T10:00:00.000Z",
            "current": 10.3,
            "voltage": 219
        }
    ],
    "rows": 1
}

使用场景:

  • ✅ 前端开发,直接使用对象属性
  • ✅ 动态列查询,列数不固定
  • ✅ 提高代码可读性

最佳实践

1. 性能优化

批量操作
# ❌ 不推荐:多次单条插入
for i in {1..100}; do
  curl -L -u root:taosdata \
    -d "INSERT INTO demo.d1001 VALUES (NOW, $i, 220, 0.3)" \
    http://localhost:6041/rest/sql
done

# ✅ 推荐:批量插入
curl -L -u root:taosdata \
  -d "INSERT INTO demo.d1001 VALUES 
      ('2024-01-22 10:00:00', 10.1, 220, 0.30)
      ('2024-01-22 10:00:01', 10.2, 219, 0.31)
      ('2024-01-22 10:00:02', 10.3, 221, 0.32)
      ... (更多数据)" \
  http://localhost:6041/rest/sql
使用连接池

Python 示例:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# 创建会话并配置连接池
session = requests.Session()
retries = Retry(total=3, backoff_factor=0.1)
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=20, max_retries=retries)
session.mount('http://', adapter)

# 使用会话发送请求
response = session.post(
    'http://localhost:6041/rest/sql',
    headers={'Authorization': 'Basic cm9vdDp0YW9zZGF0YQ=='},
    data='SELECT * FROM demo.meters'
)
限制返回结果集大小
# 使用 LIMIT 限制返回行数
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters WHERE ts >= NOW - 1d LIMIT 1000" \
  http://localhost:6041/rest/sql

2. 安全实践

使用 HTTPS

配置 taosAdapter HTTPS:

# /etc/taos/taosadapter.toml
[http]
enable = true
port = 6041

[https]
enable = true
port = 6043
certFile = "/path/to/cert.pem"
keyFile = "/path/to/key.pem"

使用 HTTPS 请求:

curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  https://localhost:6043/rest/sql
避免在 URL 中暴露密码
# ❌ 不推荐:密码可能被记录到日志
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

# ✅ 推荐:使用环境变量
export TDENGINE_USER="root"
export TDENGINE_PASS="taosdata"
export TDENGINE_AUTH=$(echo -n "$TDENGINE_USER:$TDENGINE_PASS" | base64)

curl -L -H "Authorization: Basic $TDENGINE_AUTH" \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql
使用 Token 认证(生产环境)
# 1. 创建专用 Token
taos> CREATE TOKEN app_readonly FROM USER readonly_user;

# 2. 在应用中使用 Token
export TDENGINE_TOKEN="bjUvkeBfqFsrXBSj8QjnORJcN0nyA6vdkLNAbkI2MhbWPt289OnIQcZHDIDa8SR"

curl -L -H "Authorization: Bearer $TDENGINE_TOKEN" \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

# 3. 需要时撤销 Token
taos> DROP TOKEN app_readonly;

3. 错误处理

完整的错误处理示例(Python)
import requests
import json

def execute_sql(sql, database=None):
    """执行 TDengine SQL 语句"""
    url = f"http://localhost:6041/rest/sql"
    if database:
        url += f"/{database}"
    
    headers = {
        'Authorization': 'Basic cm9vdDp0YW9zZGF0YQ=='
    }
    
    try:
        response = requests.post(url, headers=headers, data=sql, timeout=30)
        
        # 检查 HTTP 状态码
        if response.status_code == 401:
            return {"success": False, "error": "认证失败,请检查用户名和密码"}
        elif response.status_code == 404:
            return {"success": False, "error": "接口不存在,请检查 URL"}
        elif response.status_code == 503:
            return {"success": False, "error": "服务不可用,请稍后重试"}
        elif response.status_code != 200:
            return {"success": False, "error": f"HTTP 错误:{response.status_code}"}
        
        # 解析响应
        result = response.json()
        
        # 检查业务状态码
        if result.get('code') == 0:
            return {
                "success": True,
                "data": result.get('data', []),
                "rows": result.get('rows', 0),
                "columns": result.get('column_meta', [])
            }
        else:
            return {
                "success": False,
                "error": f"SQL 执行错误 [{result.get('code')}]: {result.get('desc', '未知错误')}"
            }
    
    except requests.exceptions.Timeout:
        return {"success": False, "error": "请求超时"}
    except requests.exceptions.ConnectionError:
        return {"success": False, "error": "连接失败,请检查服务是否运行"}
    except json.JSONDecodeError:
        return {"success": False, "error": "响应格式错误"}
    except Exception as e:
        return {"success": False, "error": f"未知错误: {str(e)}"}

# 使用示例
result = execute_sql("SELECT * FROM demo.meters LIMIT 10", database="demo")
if result['success']:
    print(f"查询成功,返回 {result['rows']} 行数据")
    for row in result['data']:
        print(row)
else:
    print(f"查询失败:{result['error']}")

4. 监控和日志

启用详细日志
# /etc/taos/taosadapter.toml
[log]
level = "debug"  # trace, debug, info, warn, error
path = "/var/log/taos"
rotationCount = 30
rotationSize = "1GB"
监控关键指标
# 监控 taosAdapter 进程
ps aux | grep taosadapter

# 监控端口连接数
netstat -an | grep :6041 | wc -l

# 查看最近的错误日志
tail -n 100 /var/log/taos/taosadapter.log | grep ERROR

# 监控请求延迟(在日志中)
tail -f /var/log/taos/taosadapter.log | grep "cost"

常见问题

Q1: 为什么返回 401 未授权错误?

可能原因:

  1. Base64 编码错误
  2. 用户名或密码错误
  3. 用户权限不足

解决方法:

# 1. 验证 Base64 编码
echo -n "root:taosdata" | base64
# 输出应该是:cm9vdDp0YW9zZGF0YQ==

# 2. 使用 taos CLI 验证账号
taos -u root -p taosdata

# 3. 检查用户权限
taos> SHOW USERS;

Q2: 为什么 USE 命令不生效?

REST API 是无状态的,USE 命令不会生效。

解决方法:

# 方法 1:在 SQL 中使用完整表名
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

# 方法 2:在 URL 中指定数据库
curl -L -u root:taosdata \
  -d "SELECT * FROM meters" \
  http://localhost:6041/rest/sql/demo

Q3: 如何处理大量数据的插入?

建议:

  1. 批量插入,每批 1000-10000 条
  2. 使用多线程并发插入
  3. 优化数据格式,减少 JSON 解析开销
# Python 批量插入示例
def batch_insert(data_list, batch_size=5000):
    for i in range(0, len(data_list), batch_size):
        batch = data_list[i:i+batch_size]
        values = ", ".join([f"('{row[0]}', {row[1]}, {row[2]}, {row[3]})" 
                           for row in batch])
        sql = f"INSERT INTO demo.d1001 VALUES {values}"
        execute_sql(sql)

Q4: 时间戳格式问题

问题: 时间戳显示为 UTC 时区,如何转换?

解决方法:

# 方法 1:使用 tz 参数
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  "http://localhost:6041/rest/sql?tz=Asia/Shanghai"

# 方法 2:在客户端转换
# Python 示例
from datetime import datetime
import pytz

utc_time = "2024-01-22T10:00:00.000Z"
dt = datetime.fromisoformat(utc_time.replace('Z', '+00:00'))
local_time = dt.astimezone(pytz.timezone('Asia/Shanghai'))
print(local_time)  # 2024-01-22 18:00:00+08:00

Q5: 如何实现负载均衡?

使用 Nginx 作为反向代理:

# /etc/nginx/nginx.conf
upstream tdengine_api {
    least_conn;  # 最少连接数算法
    server 192.168.1.10:6041 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:6041 weight=1 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:6041 weight=1 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name tdengine.example.com;

    location /rest/ {
        proxy_pass http://tdengine_api;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
}

Q6: 如何调试 REST API 请求?

方法 1:使用 curl 的详细输出

curl -v -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  http://localhost:6041/rest/sql

方法 2:使用 HTTPie(更友好的输出)

# 安装 HTTPie
pip install httpie

# 发送请求
http POST http://localhost:6041/rest/sql \
  Authorization:"Basic cm9vdDp0YW9zZGF0YQ==" \
  Content-Type:text/plain \
  --raw "SELECT * FROM demo.meters"

方法 3:使用 Postman

  1. 创建新的 POST 请求
  2. URL:http://localhost:6041/rest/sql
  3. Headers:
    • Authorization: Basic cm9vdDp0YW9zZGF0YQ==
    • Content-Type: text/plain
  4. Body(raw):SELECT * FROM demo.meters

版本差异说明

TDengine 2.x vs 3.x

URI 支持
URITDengine 2.xTDengine 3.x
/rest/sql✅ 支持✅ 支持(响应格式不同)
/rest/sqlt✅ 支持❌ 不再支持
/rest/sqlutc✅ 支持❌ 不再支持
HTTP 状态码
状态码TDengine 2.xTDengine 3.x说明
200✅✅正常响应和错误响应
400❌✅参数错误
401❌✅认证失败
404✅✅接口不存在
500❌✅内部错误
503✅✅资源不足
响应格式差异

TDengine 2.x:

{
  "status": "succ",
  "head": ["name", "created_time", "ntables", ...],
  "data": [
    ["log", "2020-09-02 17:23:00.039", 4, ...]
  ],
  "rows": 1
}

TDengine 3.x:

{
  "code": 0,
  "column_meta": [
    ["name", "VARCHAR", 64],
    ["ntables", "BIGINT", 8],
    ...
  ],
  "data": [
    ["information_schema", 16, "ready"]
  ],
  "rows": 1
}

主要变化:

  • status → code(0 表示成功)
  • head → column_meta(包含类型和长度信息)
  • 数据格式保持一致

附录

完整示例代码

Python
import requests
import json
from typing import Optional, Dict, List, Any

class TDengineRestClient:
    """TDengine REST API 客户端"""
    
    def __init__(self, host: str = "localhost", port: int = 6041, 
                 username: str = "root", password: str = "taosdata",
                 database: Optional[str] = None, use_https: bool = False):
        self.base_url = f"{'https' if use_https else 'http'}://{host}:{port}/rest/sql"
        if database:
            self.base_url += f"/{database}"
        
        import base64
        auth_str = f"{username}:{password}"
        auth_bytes = auth_str.encode('utf-8')
        auth_b64 = base64.b64encode(auth_bytes).decode('utf-8')
        
        self.headers = {
            'Authorization': f'Basic {auth_b64}',
            'Content-Type': 'text/plain'
        }
    
    def execute(self, sql: str, tz: Optional[str] = None, 
                req_id: Optional[str] = None) -> Dict[str, Any]:
        """执行 SQL 语句"""
        url = self.base_url
        params = {}
        if tz:
            params['tz'] = tz
        if req_id:
            params['req_id'] = req_id
        
        try:
            response = requests.post(url, headers=self.headers, 
                                    data=sql, params=params, timeout=30)
            result = response.json()
            
            if result.get('code') == 0:
                return {
                    'success': True,
                    'data': result.get('data', []),
                    'rows': result.get('rows', 0),
                    'columns': result.get('column_meta', [])
                }
            else:
                return {
                    'success': False,
                    'error_code': result.get('code'),
                    'error': result.get('desc', 'Unknown error')
                }
        except Exception as e:
            return {
                'success': False,
                'error': str(e)
            }
    
    def query(self, sql: str) -> List[Any]:
        """查询数据"""
        result = self.execute(sql)
        if result['success']:
            return result['data']
        else:
            raise Exception(result.get('error', 'Query failed'))
    
    def insert(self, sql: str) -> int:
        """插入数据,返回受影响行数"""
        result = self.execute(sql)
        if result['success']:
            return result['data'][0][0] if result['data'] else 0
        else:
            raise Exception(result.get('error', 'Insert failed'))

# 使用示例
if __name__ == "__main__":
    # 创建客户端
    client = TDengineRestClient(host="localhost", database="demo")
    
    # 创建数据库和表
    client.execute("CREATE DATABASE IF NOT EXISTS demo")
    client.execute("""
        CREATE STABLE IF NOT EXISTS demo.meters (
            ts TIMESTAMP, 
            current FLOAT, 
            voltage INT, 
            phase FLOAT
        ) TAGS (
            location BINARY(64), 
            groupId INT
        )
    """)
    
    # 插入数据
    affected = client.insert("""
        INSERT INTO demo.d1001 USING demo.meters TAGS ('California.SanFrancisco', 2) 
        VALUES (NOW, 10.3, 219, 0.31)
    """)
    print(f"Inserted {affected} rows")
    
    # 查询数据
    rows = client.query("SELECT * FROM demo.meters LIMIT 10")
    for row in rows:
        print(row)
JavaScript (Node.js)
const axios = require('axios');

class TDengineRestClient {
    constructor(options = {}) {
        const {
            host = 'localhost',
            port = 6041,
            username = 'root',
            password = 'taosdata',
            database = null,
            useHttps = false
        } = options;

        this.baseURL = `${useHttps ? 'https' : 'http'}://${host}:${port}/rest/sql`;
        if (database) {
            this.baseURL += `/${database}`;
        }

        const auth = Buffer.from(`${username}:${password}`).toString('base64');
        this.headers = {
            'Authorization': `Basic ${auth}`,
            'Content-Type': 'text/plain'
        };
    }

    async execute(sql, options = {}) {
        const { tz, reqId } = options;
        const params = {};
        if (tz) params.tz = tz;
        if (reqId) params.req_id = reqId;

        try {
            const response = await axios.post(this.baseURL, sql, {
                headers: this.headers,
                params: params,
                timeout: 30000
            });

            const result = response.data;
            if (result.code === 0) {
                return {
                    success: true,
                    data: result.data || [],
                    rows: result.rows || 0,
                    columns: result.column_meta || []
                };
            } else {
                return {
                    success: false,
                    errorCode: result.code,
                    error: result.desc || 'Unknown error'
                };
            }
        } catch (error) {
            return {
                success: false,
                error: error.message
            };
        }
    }

    async query(sql) {
        const result = await this.execute(sql);
        if (result.success) {
            return result.data;
        } else {
            throw new Error(result.error || 'Query failed');
        }
    }

    async insert(sql) {
        const result = await this.execute(sql);
        if (result.success) {
            return result.data[0]?.[0] || 0;
        } else {
            throw new Error(result.error || 'Insert failed');
        }
    }
}

// 使用示例
(async () => {
    const client = new TDengineRestClient({ database: 'demo' });

    // 创建数据库和表
    await client.execute('CREATE DATABASE IF NOT EXISTS demo');
    await client.execute(`
        CREATE STABLE IF NOT EXISTS demo.meters (
            ts TIMESTAMP, 
            current FLOAT, 
            voltage INT, 
            phase FLOAT
        ) TAGS (
            location BINARY(64), 
            groupId INT
        )
    `);

    // 插入数据
    const affected = await client.insert(`
        INSERT INTO demo.d1001 USING demo.meters TAGS ('California.SanFrancisco', 2) 
        VALUES (NOW, 10.3, 219, 0.31)
    `);
    console.log(`Inserted ${affected} rows`);

    // 查询数据
    const rows = await client.query('SELECT * FROM demo.meters LIMIT 10');
    console.log(rows);
})();
Go
package main

import (
    "bytes"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

type TDengineRestClient struct {
    baseURL string
    headers map[string]string
    client  *http.Client
}

type Response struct {
    Code       int           `json:"code"`
    ColumnMeta [][]any       `json:"column_meta"`
    Data       [][]any       `json:"data"`
    Rows       int           `json:"rows"`
    Desc       string        `json:"desc"`
}

func NewTDengineRestClient(host string, port int, username, password, database string) *TDengineRestClient {
    baseURL := fmt.Sprintf("http://%s:%d/rest/sql", host, port)
    if database != "" {
        baseURL += "/" + database
    }

    auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
    
    return &TDengineRestClient{
        baseURL: baseURL,
        headers: map[string]string{
            "Authorization": "Basic " + auth,
            "Content-Type":  "text/plain",
        },
        client: &http.Client{
            Timeout: 30 * time.Second,
        },
    }
}

func (c *TDengineRestClient) Execute(sql string) (*Response, error) {
    req, err := http.NewRequest("POST", c.baseURL, bytes.NewBufferString(sql))
    if err != nil {
        return nil, err
    }

    for k, v := range c.headers {
        req.Header.Set(k, v)
    }

    resp, err := c.client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    var result Response
    err = json.Unmarshal(body, &result)
    if err != nil {
        return nil, err
    }

    if result.Code != 0 {
        return &result, fmt.Errorf("SQL error [%d]: %s", result.Code, result.Desc)
    }

    return &result, nil
}

func main() {
    client := NewTDengineRestClient("localhost", 6041, "root", "taosdata", "demo")

    // 创建数据库
    _, err := client.Execute("CREATE DATABASE IF NOT EXISTS demo")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    // 查询数据
    result, err := client.Execute("SELECT * FROM demo.meters LIMIT 10")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    fmt.Printf("Returned %d rows\n", result.Rows)
    for _, row := range result.Data {
        fmt.Println(row)
    }
}

常用 SQL 模板

# 数据库操作
CREATE DATABASE IF NOT EXISTS mydb KEEP 365 DURATION 10 BUFFER 16;
DROP DATABASE IF EXISTS mydb;
SHOW DATABASES;

# 超级表操作
CREATE STABLE IF NOT EXISTS mydb.meters (
    ts TIMESTAMP, 
    current FLOAT, 
    voltage INT
) TAGS (location BINARY(64), groupId INT);

SHOW STABLES;
DESCRIBE mydb.meters;

# 子表操作
CREATE TABLE mydb.d1001 USING mydb.meters TAGS ('Beijing', 1);
INSERT INTO mydb.d1001 VALUES (NOW, 10.3, 219);

# 查询操作
SELECT * FROM mydb.meters WHERE ts >= NOW - 1h;
SELECT AVG(current), location FROM mydb.meters WHERE ts >= NOW - 1d GROUP BY location;
SELECT FIRST(ts), LAST(ts), COUNT(*) FROM mydb.meters;

# 聚合查询
SELECT AVG(current), MAX(voltage), location 
FROM mydb.meters 
WHERE ts >= NOW - 1d 
INTERVAL(1h) 
GROUP BY location;

总结

TDengine REST API 为开发者提供了一种简单、灵活、跨平台的数据库访问方式。通过本手册,你应该已经掌握:

✅ REST API 的适用场景和优势
✅ 如何配置和使用 REST API
✅ 完整的 API 规范和参数说明
✅ 三种认证机制的使用方法
✅ 性能优化和安全最佳实践
✅ 常见问题的解决方法
✅ 多种编程语言的实现示例

推荐学习资源:

获取帮助:

  • GitHub Issues: https://github.com/taosdata/TDengine/issues
  • 论坛:https://www.taosdata.com/forum
  • 技术支持:support@taosdata.com

关于 TDengine

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

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐