1、简介

TDengine 是一款开源、云原生的时序数据库,专为物联网、工业互联网、金融、IT 运维监控等场景设计并优化。它能让大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据得到高效实时的处理,对业务的运行状态进行实时的监测、预警,从大数据中挖掘出商业价值。

2、开发指南

开发一个应用,如果你准备采用 TDengine 作为时序数据处理的工具,那么有如下几个事情要做:

  • 确定应用到 TDengine 的连接方式。无论你使用何种编程语言,你总是可以使用 REST 接口, 但也可以使用每种编程语言独有的连接器进行方便的连接。
  • 根据自己的应用场景,确定数据模型。根据数据特征,决定建立一个还是多个库;分清静态标签、采集量,建立正确的超级表,建立子表。
  • 决定插入数据的方式。TDengine 支持使用标准的 SQL 写入,但同时也支持 Schemaless 模式写入,这样不用手工建表,可以将数据直接写入。

2.1 连接器建立连接的方式

TDengine 提供了丰富的应用程序开发接口,为了便于用户快速开发自己的应用,TDengine 支持了多种编程语言的连接器,其中官方连接器包括支持 C/C++、Java、Python、Go、Node.js、C#、Rust、Lua(社区贡献)和 PHP (社区贡献)的连接器。这些连接器支持使用原生接口(taosc)和 REST 接口(部分语言暂不支持)连接 TDengine 集群。社区开发者也贡献了多个非官方连接器,例如 ADO.NET 连接器、Lua 连接器和 PHP 连接器。

连接器建立连接的方式,TDengine 提供两种:

  • 通过 taosAdapter 组件提供的 REST API 建立与 taosd 的连接,这种连接方式下文中简称“REST 连接”
  • 通过客户端驱动程序 taosc 直接与服务端程序 taosd 建立连接,这种连接方式下文中简称“原生连接”。

关键不同点在于:

  • 使用 REST 连接,用户无需安装客户端驱动程序 taosc,具有跨平台易用的优势,但性能要下降 30% 左右。
  • 使用原生连接可以体验 TDengine 的全部功能,如参数绑定接口、订阅等等。

2.2 安装客户端驱动 taosc

如果选择原生连接,而且应用程序不在 TDengine 同一台服务器上运行,你需要先安装客户端驱动,否则可以跳过此一步。为避免客户端驱动和服务端不兼容,请使用一致的版本。

默认安装路径为:C:\TDengine,其中包括以下文件(目录):
taos.exe:TDengine CLI 命令行程序
taosadapter.exe:提供 RESTful 服务和接受其他多种软件写入请求的服务端可执行文件
taosBenchmark.exe:TDengine 测试程序
cfg : 配置文件目录
driver: 应用驱动动态链接库
examples: 示例程序 bash/C/C#/go/JDBC/Python/Node.js
include: 头文件
log : 日志文件
unins000.exe: 卸载程序
  • 配置 taos.cfg 编辑 taos.cfg 文件(默认路径 C:\TDengine\cfg\taos.cfg),将 firstEP 修改为 TDengine 服务器的 End Point,例如:h1.tdengine.com:6030。

如利用 FQDN 连接服务器,必须确认本机网络环境 DNS 已配置好,或在 hosts 文件中添加 FQDN 寻址记录, 如编辑 C:\Windows\system32\drivers\etc\hosts,添加类似如下的记录:192.168.1.99 h1.taos.com

  • 安装验证 在 cmd 下进入到 C:\TDengine 目录下直接执行 taos.exe,连接到 TDengine 服务,进入到 TDengine CLI 界面,示例如下:
taos> show databases;

在这里插入图片描述

taos> use test;
taos> show tables;

在这里插入图片描述

2.3 安装连接器

使用 pip 从 PyPI 安装:

pip install taospy

在这里插入图片描述

从 Git URL 安装:

pip install git+https://github.com/taosdata/taos-connector-python.git

2.4 建立连接(原生连接)

在执行这一步之前,请确保有一个正在运行的,且可以访问到的 TDengine,而且服务端的 FQDN 配置正确。以下示例代码,都假设 TDengine 安装在本机,且 FQDN(默认 localhost) 和 serverPort(默认 6030) 都使用默认配置。

import taos

def test_connection():
    # all parameters are optional.
    # if database is specified,
    # then it must exist.
    conn = taos.connect(host="localhost",
                        port=6030,
                        user="root",
                        p1assword="taosdata",
                        database="log")
    print('client info:', conn.client_info)
    print('server info:', conn.server_info)
    conn.close()


if __name__ == "__main__":
    test_connection()

在这里插入图片描述

2.5 创建库

TDengine 采用类关系型数据模型,需要建库、建表。因此对于一个具体的应用场景,需要考虑库、超级表和普通表的设计。

为了在各种场景下 TDengine 都能以最大效率工作,TDengine 建议将不同数据特征的表创建在不同的库里。

CREATE DATABASE power KEEP 365 DURATION 10 BUFFER 16 WAL_LEVEL 1;
USE power;

在这里插入图片描述

上述语句将创建一个名为 power 的库,这个库的数据将保留 365 天(超过 365 天将被自动删除),每 10 天一个数据文件,每个 VNode 的写入内存池的大小为 16 MB,对该数据库入会写 WAL 但不执行 FSYNC。

2.6 创建超级表

一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用 TDengine, 需要对每个类型的数据采集点创建一个超级表。

CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);

与创建普通表一样,创建超级表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 Schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。

2.7 创建表

TDengine 对每个数据采集点需要独立建表。与标准的关系型数据库一样,一张表有表名,Schema,但除此之外,还可以带有一到多个标签。创建时,需要使用超级表做模板,同时指定标签的具体值。

CREATE TABLE d1001 USING meters TAGS ("California.SanFrancisco", 2);

在这里插入图片描述

其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值为 "California.SanFrancisco",标签 groupId 的具体标签值为 2。

2.8 自动建表

在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表且后面的 USING 语句被忽略。

INSERT INTO d1002 USING meters TAGS ("California.SanFrancisco", 2) VALUES (NOW, 10.2, 219, 0.32);

在这里插入图片描述

2.9 查询数据

在命令行工具 taos 中,从表 d1001 中查询出 voltage > 215 的记录,按时间降序排列,仅仅输出 2 条。

taos> select * from d1001 where voltage > 215 order by ts desc limit 2;

在这里插入图片描述

在 TDengine CLI,查找加利福尼亚州所有智能电表采集的电压平均值,并按照 location 分组。

taos> SELECT AVG(voltage), location FROM meters GROUP BY location;

在这里插入图片描述

在 TDengine CLI, 查找 groupId 为 2 的所有智能电表的记录条数,电流的最大值。

taos> SELECT count(*), max(current) FROM meters where groupId = 2;

在这里插入图片描述

通过迭代逐行获取查询结果。我们创建了 power 数据库,并向 meters 表写入了一些数据,以下示例代码展示如何查询这个表的数据。

import taos

def test_connection():
    # all parameters are optional.
    # if database is specified,
    # then it must exist.
    conn = taos.connect(host="localhost",
                        port=6030,
                        user="root",
                        p1assword="taosdata",
                        database="test")
    print('client info:', conn.client_info)
    print('server info:', conn.server_info)

    query_api_demo(conn)
    conn.close()

def query_api_demo(conn: taos.TaosConnection):
    result: taos.TaosResult = conn.query("SELECT tbname, * FROM meters LIMIT 2")
    print("field count:", result.field_count)
    print("meta of fields[1]:", result.fields[1])
    print("======================Iterate on result=========================")
    for row in result:
        print(row)

if __name__ == "__main__":
    test_connection()

在这里插入图片描述

一次获取所有查询结果,并把每一行转化为一个字典返回。

import taos

def test_connection():
    # all parameters are optional.
    # if database is specified,
    # then it must exist.
    conn = taos.connect(host="localhost",
                        port=6030,
                        user="root",
                        p1assword="taosdata",
                        database="test")
    print('client info:', conn.client_info)
    print('server info:', conn.server_info)

    fetch_all_demo(conn)
    conn.close()

def fetch_all_demo(conn: taos.TaosConnection):
    result: taos.TaosResult = conn.query("SELECT ts, current FROM meters LIMIT 10")
    rows = result.fetch_all_into_dict()
    print("row count:", result.row_count)
    print("===============all data===================")
    print(rows)

if __name__ == "__main__":
    test_connection()

在这里插入图片描述

2.10 连接器简介

TDengine 提供了丰富的应用程序开发接口,为了便于用户快速开发自己的应用,TDengine 支持了多种编程语言的连接器,其中官方连接器包括支持 C/C++、Java、Python、Go、Node.js、C# 和 Rust 的连接器。这些连接器支持使用原生接口(taosc)和 REST 接口(部分语言暂不支持)连接 TDengine 集群。社区开发者也贡献了多个非官方连接器,例如 ADO.NET 连接器、Lua 连接器和 PHP 连接器。 在这里插入图片描述 <font color=blue> taospy 是 TDengine 的官方 Python 连接器。taospy 提供了丰富的 API, 使得 Python 应用可以很方便地使用 TDengine。taospy 对 TDengine 的原生接口和 REST 接口都进行了封装, 分别对应 taospy 包的 taos 模块 和 taosrest 模块。 除了对原生接口和 REST 接口的封装,taospy 还提供了符合 Python 数据访问规范(PEP 249) 的编程接口。这使得 taospy 和很多第三方工具集成变得简单,比如 SQLAlchemy 和 pandas。

<font color=green>taos-ws-py 是使用 WebSocket 方式连接 TDengine 的 Python 连接器包。可以选装。

<font color=pink>使用客户端驱动提供的原生接口直接与服务端建立的连接的方式下文中称为“原生连接”;使用 taosAdapter 提供的 REST 接口或 WebSocket 接口与服务端建立的连接的方式下文中称为“REST 连接”或“WebSocket 连接”。

  • 支持的平台 原生连接支持的平台和 TDengine 客户端支持的平台一致。 REST 连接支持所有能运行 Python 的平台。

  • 支持的功能 原生连接支持 TDengine 的所有核心功能, 包括: 连接管理、执行 SQL、参数绑定、订阅、无模式写入(schemaless)。 REST 连接支持的功能包括:连接管理、执行 SQL。 (通过执行 SQL 可以: 管理数据库、管理表和超级表、写入数据、查询数据、创建连续查询等)。

pip3 uninstall taos taospy
pip3 install taospy
pip3 install taospy==2.3.0
pip3 install taospy[ws]
pip3 install taos-ws-py

在这里插入图片描述

对于原生连接,需要验证客户端驱动和 Python 连接器本身是否都正确安装。如果能成功导入 taos 模块,则说明已经正确安装了客户端驱动和 Python 连接器。可在 Python 交互式 Shell 中输入:

import taos

对于 REST 连接,只需验证是否能成功导入 taosrest 模块。可在 Python 交互式 Shell 中输入:

import taosrest

对于 WebSocket 连接,只需验证是否能成功导入 taosws 模块。可在 Python 交互式 Shell 中输入:

import taosws

2.11 连接器(原生连接:taos)

(1)原生连接 请确保 TDengine 集群已经启动, 且集群中机器的 FQDN (如果启动的是单机版,FQDN 默认为 hostname)在本机能够解析, 可用 ping 命令进行测试:

ping <FQDN>

然后测试用 TDengine CLI 能否正常连接集群:

taos -h <FQDN> -p <PORT>
taos -h localhost -P 6030

在这里插入图片描述

上面的 FQDN 可以为集群中任意一个 dnode 的 FQDN, PORT 为这个 dnode 对应的 serverPort。

import taos

conn: taos.TaosConnection = taos.connect(host="localhost",
                                         user="root",
                                         p1assword="taosdata",
                                         database="test",
                                         port=6030,
                                         config="/etc/taos",  # for windows the default value is C:\TDengine\cfg
                                         timezone="Asia/Shanghai")  # default your host's timezone

server_version = conn.server_info
print("server_version", server_version)
client_version = conn.client_info
print("client_version", client_version)  # 3.0.0.0

conn.close()

conn = taos.connect()
# Execute a sql, ignore the result set, just get affected rows. It's useful for DDL and DML statement.
conn.execute("DROP DATABASE IF EXISTS test")
conn.execute("CREATE DATABASE test")
# change database. same as execute "USE db"
conn.select_db("test")
conn.execute("CREATE STABLE weather(ts TIMESTAMP, temperature FLOAT) TAGS (location INT)")
affected_row = conn.execute("INSERT INTO t1 USING weather TAGS(1) VALUES (now, 23.5) (now+1m, 23.5) (now+2m, 24.4)")
print("affected_row", affected_row)
# output:
# affected_row 3

2.12 连接器(REST连接:taosrest )

(2)REST 连接 对于 REST 连接, 除了确保集群已经启动,还要确保 taosAdapter 组件已经启动。可以使用如下 curl 命令测试:

在安装文件内找到taosAdapter .exe。 在这里插入图片描述 运行taosAdapter.exe之后的界面如下: 在这里插入图片描述 输入如下的命令:

# curl -u root:taosdata http://<FQDN>:<PORT>/rest/sql -d "select server_version()"
curl -u root:taosdata http://localhost:6030/rest/sql -d "select server_version()"

在这里插入图片描述

上面的 FQDN 为运行 taosAdapter 的机器的 FQDN, PORT 为 taosAdapter 配置的监听端口, 默认为 6041。 如果测试成功,会输出服务器版本信息,比如:

from taosrest import connect, TaosRestConnection, TaosRestCursor

conn = connect(url="http://localhost:6041",
               user="root",
               p1assword="taosdata",
               timeout=30)

# create STable
cursor = conn.cursor()
cursor.execute("DROP DATABASE IF EXISTS power")
cursor.execute("CREATE DATABASE power")
cursor.execute(
    "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (location BINARY(64), groupId INT)")

# insert data
cursor.execute("""INSERT INTO power.d1001 USING power.meters TAGS('California.SanFrancisco', 2) VALUES ('2018-10-03 14:38:05.000', 10.30000, 219, 0.31000) ('2018-10-03 14:38:15.000', 12.60000, 218, 0.33000) ('2018-10-03 14:38:16.800', 12.30000, 221, 0.31000)
    power.d1002 USING power.meters TAGS('California.SanFrancisco', 3) VALUES ('2018-10-03 14:38:16.650', 10.30000, 218, 0.25000)
    power.d1003 USING power.meters TAGS('California.LosAngeles', 2) VALUES ('2018-10-03 14:38:05.500', 11.80000, 221, 0.28000) ('2018-10-03 14:38:16.600', 13.40000, 223, 0.29000)
    power.d1004 USING power.meters TAGS('California.LosAngeles', 3) VALUES ('2018-10-03 14:38:05.000', 10.80000, 223, 0.29000) ('2018-10-03 14:38:06.500', 11.50000, 221, 0.35000)""")
print("inserted row count:", cursor.rowcount)

# query data
cursor.execute("SELECT * FROM power.meters LIMIT 3")

# get total rows
print("queried row count:", cursor.rowcount)

# get column names from cursor
column_names = [meta[0] for meta in cursor.description]

# get rows
data = cursor.fetchall()
print(column_names)
for row in data:
    print(row)

conn.close()

在这里插入图片描述

以上示例代码假设 TDengine 安装在本机, 且 FQDN 和 serverPort 都使用了默认配置。

2.12 连接器(REST连接:RestClient )

RestClient 类是对于 REST API 的直接封装。它只包含一个 sql() 方法用于执行任意 SQL 语句, 并返回执行结果。

from taosrest import RestClient

client = RestClient("http://localhost:6041", user="root", p1assword="taosdata")
res: dict = client.sql("SELECT ts, current FROM power.meters LIMIT 1")
print(res)

在这里插入图片描述

2.13 连接器(pandas )

  • REST连接
import pandas
from sqlalchemy import create_engine, text

engine = create_engine("taosrest://root:taosdata@localhost:6041")
conn = engine.connect()
df: pandas.DataFrame = pandas.read_sql(text("SELECT * FROM power.meters"), conn)
conn.close()

# print index
print(df.index)
# print data type  of element in ts column
print(type(df.ts[0]))
print(df.head(3))

在这里插入图片描述

  • 原生连接
import pandas
from sqlalchemy import create_engine, text

engine = create_engine("taos://root:taosdata@localhost:6030/power")
conn = engine.connect()
df = pandas.read_sql(text("SELECT * FROM power.meters"), conn)
conn.close()


# print index
print(df.index)
# print data type  of element in ts column
print(type(df.ts[0]))
print(df.head(3))

在这里插入图片描述

2.14 异常处理

所有数据库操作如果出现异常,都会直接抛出来。由应用程序负责异常处理。比如:

import taos

try:
    conn = taos.connect()
    conn.execute("CREATE TABLE 123")  # wrong sql
except taos.Error as e:
    print(e)
    print("exception class: ", e.__class__.__name__)
    print("error number:", e.errno)
    print("error message:", e.msg)
except BaseException as other:
    print("exception occur")
    print(other)

在这里插入图片描述

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭ 如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O??? 如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡) 感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!