TDengine REST API 使用手册

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 运行状态
bash 复制代码
# 检查 TDengine Server
systemctl status taosd

# 如未启动,执行启动命令
systemctl start taosd
2. 启动 taosAdapter 服务
bash 复制代码
# 启动 taosAdapter
systemctl start taosadapter

# 查看服务状态
systemctl status taosadapter

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

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

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"

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

bash 复制代码
systemctl restart taosadapter

快速入门

连接验证

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

bash 复制代码
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 编码

成功响应示例:

json 复制代码
{
    "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. 创建数据库
bash 复制代码
curl -L -H "Authorization: Basic cm9vdDp0YW9zZGF0YQ==" \
  -d "CREATE DATABASE IF NOT EXISTS demo KEEP 365 DURATION 10 BUFFER 16" \
  http://localhost:6041/rest/sql

响应:

json 复制代码
{
    "code": 0,
    "column_meta": [["affected_rows", "INT", 4]],
    "data": [[0]],
    "rows": 1
}
2. 创建超级表(Super Table)
bash 复制代码
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. 创建子表并插入数据

单条插入:

bash 复制代码
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

批量插入:

bash 复制代码
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. 查询数据

基础查询:

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

聚合查询:

bash 复制代码
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 语句中可以省略数据库前缀:

bash 复制代码
# 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 格式
text 复制代码
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+) truefalse

URL 示例:

bash 复制代码
# 基础 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)

必需的请求头:

http 复制代码
Content-Type: text/plain
Authorization: <认证类型> <TOKEN>

可选的请求头:

http 复制代码
Accept: application/json
Accept-Encoding: gzip, deflate
请求体(Body)

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

sql 复制代码
SELECT * FROM demo.meters WHERE ts >= NOW - 1h;

注意事项:

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

认证机制

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

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

格式:

http 复制代码
Authorization: Basic <TOKEN>

TOKEN 生成:

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

使用示例:

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

或使用 -u 参数:

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

优势:

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

创建 Token:

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

返回示例:

text 复制代码
token: bjUvkeBfqFsrXBSj8QjnORJcN0nyA6vdkLNAbkI2MhbWPt289OnIQcZHDIDa8SR

使用 Token:

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

Token 管理:

sql 复制代码
-- 查看 Token 列表
SHOW TOKENS;

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

获取授权码:

bash 复制代码
curl http://localhost:6041/rest/login/root/taosdata

响应:

json 复制代码
{
  "code": 0,
  "desc": "/KfeAzX/f9na8qdtNZmtONryp201ma04bEl8LcvLUd7a8qdtNZmtONryp201ma04"
}

使用授权码:

bash 复制代码
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
响应体格式

成功执行插入操作:

json 复制代码
{
  "code": 0,
  "column_meta": [["affected_rows", "INT", 4]],
  "data": [[5]],
  "rows": 1
}

成功执行查询操作:

json 复制代码
{
  "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
}

执行错误:

json 复制代码
{
  "code": 9728,
  "desc": "syntax error near \"FORM\""
}

响应字段说明:

字段 类型 说明
code int 返回码,0 表示成功,非 0 表示错误
column_meta array 列元数据,格式:[列名, 类型, 长度]
data array 查询结果数据,二维数组
rows int 返回的行数
desc string 错误描述(仅错误时)
数据类型对照表
TDengine 类型 REST API 字符串 JSON 数据类型 说明
TIMESTAMP "TIMESTAMP" string RFC3339 格式
BOOL "BOOL" boolean true/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" string Unicode 字符串
JSON "JSON" object JSON 对象
VARBINARY "VARBINARY" string Hex 字符串
GEOMETRY "GEOMETRY" string WKB Hex 字符串
BLOB "BLOB" string Hex 字符串
DECIMAL "DECIMAL(M,N)" string 定点数

特殊类型示例:

GEOMETRY 类型:

bash 复制代码
# 准备数据
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

响应:

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

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


高级功能

时区设置

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

bash 复制代码
# 使用上海时区
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/ShanghaiAmerica/New_York
  • UTC 偏移量:UTCUTC+8UTC-5

请求追踪

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

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

优势:

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

查看日志:

bash 复制代码
# taosAdapter 日志位置
tail -f /var/log/taos/taosadapter.log | grep "req_20240122"

Key-Value 格式返回

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

标准格式(默认):

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

响应:

json 复制代码
{
    "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 格式:

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

响应:

json 复制代码
{
    "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. 性能优化

批量操作
bash 复制代码
# ❌ 不推荐:多次单条插入
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 示例:

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'
)
限制返回结果集大小
bash 复制代码
# 使用 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:

toml 复制代码
# /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 请求:

bash 复制代码
curl -L -u root:taosdata \
  -d "SELECT * FROM demo.meters" \
  https://localhost:6043/rest/sql
避免在 URL 中暴露密码
bash 复制代码
# ❌ 不推荐:密码可能被记录到日志
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 认证(生产环境)
bash 复制代码
# 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)
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. 监控和日志

启用详细日志
toml 复制代码
# /etc/taos/taosadapter.toml
[log]
level = "debug"  # trace, debug, info, warn, error
path = "/var/log/taos"
rotationCount = 30
rotationSize = "1GB"
监控关键指标
bash 复制代码
# 监控 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. 用户权限不足

解决方法:

bash 复制代码
# 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 命令不会生效。

解决方法:

bash 复制代码
# 方法 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 复制代码
# 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 时区,如何转换?

解决方法:

bash 复制代码
# 方法 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 作为反向代理:

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 的详细输出

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

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

bash 复制代码
# 安装 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 支持
URI TDengine 2.x TDengine 3.x
/rest/sql ✅ 支持 ✅ 支持(响应格式不同)
/rest/sqlt ✅ 支持 ❌ 不再支持
/rest/sqlutc ✅ 支持 ❌ 不再支持
HTTP 状态码
状态码 TDengine 2.x TDengine 3.x 说明
200 正常响应和错误响应
400 参数错误
401 认证失败
404 接口不存在
500 内部错误
503 资源不足
响应格式差异

TDengine 2.x:

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

TDengine 3.x:

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

主要变化:

  • statuscode(0 表示成功)
  • headcolumn_meta(包含类型和长度信息)
  • 数据格式保持一致

附录

完整示例代码

Python
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)
javascript 复制代码
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
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 模板

bash 复制代码
# 数据库操作
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 规范和参数说明

✅ 三种认证机制的使用方法

✅ 性能优化和安全最佳实践

✅ 常见问题的解决方法

✅ 多种编程语言的实现示例

推荐学习资源:

获取帮助:

关于 TDengine

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

相关推荐
启芯硬件1 小时前
三极管和MOS管的降额使用设计实战
大数据·人工智能·经验分享·职场和发展·硬件工程
MDIOT旗舰1 小时前
全面解析ZL400边缘网关:高性价比的物联网数据采集解决方案
物联网·mqtt·边缘计算·数据采集·modbus·远程运维·物联网网关
扑火的小飞蛾1 小时前
PostgreSQL 16.1 安装指南
数据库·postgresql
大猫子的技术日记1 小时前
Redis 快速上手:5 分钟掌握核心能力
数据结构·数据库·redis·缓存·持久化·pub/sub
panzer_maus1 小时前
Redis介绍(4)-认识Redis的事务
数据库·redis·缓存
zgl_200537791 小时前
源代码:ZGLanguage 解析SQL数据血缘 之 显示 UPDATE SQL 结构图
大数据·数据库·数据仓库·hadoop·数据治理·sql解析·数据血缘
Jan123.2 小时前
数据库性能优化实战:从索引到SQL的全维度进阶
数据库·sql·性能优化
翼龙云_cloud2 小时前
亚马逊云渠道商:RDS 三大数据库引擎深度对比 MySQL/PostgreSQL/SQL Server 如何选?
数据库·mysql·postgresql·aws
GIS工具-gistools20212 小时前
欧洲电厂分布数据及绿色能源情况
大数据·gis·能源