每一个你不满意的现在,都有一个你不努力的曾经。

PHP TDengine PDO驱动


https://github.com/bearlord/pdo_taos.git

简体中文

PDO_TAOS是 涛思数据 的PDO驱动。

涛思数据 已同意使用库文件开发PHP扩展。

TDengine 简介

TDengine是涛思数据专为物联网、车联网、工业互联网、IT运维等设计和优化的大数据平台。除核心的快10倍以上的时序数据库功能外,还提供缓存、数据订阅、流式计算等功能,最大程度减少研发和运维的复杂度,且核心代码,包括集群功能全部开源(开源协议,AGPL v3.0)。

  • 10 倍以上性能提升。定义了创新的数据存储结构,单核每秒就能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快了十倍以上。
  • 硬件或云服务成本降至1/5。由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10。
  • 全栈时序数据处理引擎。将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成Kafka/Redis/HBase/Spark等软件,大幅降低应用开发和维护成本。
  • 强大的分析功能。无论是十年前还是一秒钟前的数据,指定时间范围即可查询。数据可在时间轴上或多个设备上进行聚合。即席查询可通过Shell/Python/R/Matlab随时进行。
  • 与第三方工具无缝连接。不用一行代码,即可与Telegraf, Grafana, EMQ X, Prometheus, Matlab, R集成。后续还将支持MQTT, OPC, Hadoop,Spark等, BI工具也将无缝连接。
  • 零运维成本、零学习成本。安装、集群一秒搞定,无需分库分表,实时备份。标准SQL,支持JDBC,RESTful,支持Python/Java/C/C++/Go/Node.JS, 与MySQL相似,零学习成本。

安装 PDO_TAOS

源码编译安装

phpize
./configure
make && make install

开启 pdo_taos 扩展

编辑 php.ini 文件 或者在 php.d 目录下新增配置文件。

extension=pdo_taos.so

安装TDengine

PDO_TAOS编译时,需要libtaos.so,默认路径为:/usr/lib/libtaos.so,指向路径为:/usr/local/taos/driver/libtaos.so.2.x.x.x。所以需要先安装TDengine数据库服务器端或者C客户端。

用法

0. 创建数据库

客户端完成创建数据库

CREATE DATABASE demo;

1. 连接TDengine数据库

$dbh = new PDO("taos:host=127.0.0.1;dbname=demo", "root", "taosdata");

2. CREATE 创建数据表

创建数据表与 MySQL 语法类似,数据类型也有类似,但是又有区别。

TDengine 有自己的数据库类型,NCHAR 类似 MySQL 的 VARCHARBINARY 类似 MySQL 的 TEXT

TDengine 没有 PRIMARY KEYAUTO INCREMENT, DEFAULT NULL 等关键字。

TDengine的第一列必须是 TIMESTAMP 类型。

例如:

$dbh = new PDO("taos:host=127.0.0.1;dbname=demo", "root", "taosdata");

$sql = "create table device_log_1000 (
created_timestamp TIMESTAMP,
v_bool BOOL,
v_tinyint TINYINT,
v_smallint SMALLINT,
v_int INT,
v_bigint BIGINT,
v_float FLOAT,
v_double DOUBLE,
v_binary BINARY(60),
v_nchar NCHAR(40))";
$result = $dbh->exec($sql);

var_dump($result);

如果执行成功,$result 返回 0 ; 如果失败了,返回 -1

执行结束后,登录 TDengine 数据库客户端查看数据表结构。

taos> USE DEMO;
Database changed.

taos> SHOW CREATE TABLE device_log_1000;
             Table              |          Create Table          |
==================================================================
 device_log_1000                | create table device_log_100... |
Query OK, 1 row(s) in set (0.002146s)

taos> SHOW CREATE TABLE device_log_1000\G;
*************************** 1.row ***************************
       Table: device_log_1000
Create Table: create table device_log_1000 (created_timestamp TIMESTAMP,v_bool BOOL,v_tinyint TINYINT,v_smallint SMALLINT,v_int INT,v_bigint BIGINT,v_float FLOAT,v_double DOUBLE,v_binary BINARY(60),v_nchar NCHAR(40))
Query OK, 1 row(s) in set (0.000267s)

taos> 

SHOW CREATE TABLE 可以查看表结构,显示不全,末尾请加\G

3. INSERT 插入数据

插入数据分两种方式:

  1. 调用 PDO::exec 方法直接执行 INSERT SQL语句。
  2. 调用PDO::prepare方法执行预处理操作。

3.1 PDO::exec方式

$dbh = new PDO("taos:host=127.0.0.1;dbname=demo", "root", "taosdata");

$sql = "INSERT INTO device_log_100 (
created_timestamp,
v_bool,
v_tinyint,
v_smallint,
v_int,
v_bigint,
v_float,
v_double,
v_binary,
v_nchar
) VALUES (
NOW,
1,
120,
1200,
12000,
120000,
9.09,
9.000009,
'hello TDengine',
'hello PHP'
)";

$result = $dbh->exec($sql);
var_dump($result);

执行如果成功,$result 返回 0 ; 如果失败了,返回 -1

没有方法能获取受影响的行数。

注意:

一定要调用 exec 方法,而不是 query 方法。

3.2 PDO::prepare预处理方式

<?php try {
    $dbh = new PDO("taos:host=127.0.0.1;dbname=demo", "root", "taosdata");

    $sql = "INSERT INTO device_log_100 (created_timestamp, v_bool, v_tinyint, v_smallint, v_int, v_bigint, v_float, v_double, v_binary, v_nchar ) VALUES (
    ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    $sth = $dbh->prepare($sql);

    $v1 = intval(microtime(true) * 1000);
    $v2 = 1;
    $v3 = 100;
    $v4 = 2000;
    $v5 = 20000;
    $v6 = 200000;
    $v7 = 8.08;
    $v8 = 8.000008;
    $v9 = "TDengine test";
    $v10 = "Taos test";

    $sth->bindParam(1, $v1, PDO::PARAM_TAOS_TIMESTAMP);
    $sth->bindParam(2, $v2, PDO::PARAM_TAOS_BOOL);
    $sth->bindParam(3, $v3, PDO::PARAM_TAOS_TINYINT);
    $sth->bindParam(4, $v4, PDO::PARAM_TAOS_SMALLINT);
    $sth->bindParam(5, $v5, PDO::PARAM_TAOS_INT);
    $sth->bindParam(6, $v6, PDO::PARAM_TAOS_BIGINT);
    $sth->bindParam(7, $v7, PDO::PARAM_TAOS_FLOAT);
    $sth->bindParam(8, $v8, PDO::PARAM_TAOS_DOUBLE);
    $sth->bindParam(9, $v9, PDO::PARAM_TAOS_BINARY);
    $sth->bindParam(10, $v10, PDO::PARAM_TAOS_NCHAR);

    $result = $sth->execute();
    var_dump($result);

    $rowCount = $sth->rowCount()
    var_dump($rowCount);
} catch (Exception $e) {
    printf("%d, %s\n", $e->getCode(), $e->getMessage());
}
?>

执行成功,$result 返回 true,执行失败,返回false。

$rowCount 表示受影响的行数。

占位符

支持应 :name 形式的参数名,也支持 ? 形式 。

数据类型

请忘记:PDO::PARAM_BOOLPDO::PARAM_INT, PDO::PARAM_STR 等PDO预定义类型。

请使用以下的自定义PDO类型:

PDO自定义类型 TDengine数据类型 说明
PARAM_TAOS_NULL TSDB_DATA_TYPE_NULL NULL
PARAM_TAOS_BOOL TSDB_DATA_TYPE_BOOL BOOL
PARAM_TAOS_TINYINT TSDB_DATA_TYPE_TINYINT TINYINT
PARAM_TAOS_SMALLINT TSDB_DATA_TYPE_SMALLINT SMALLINT
PARAM_TAOS_INT TSDB_DATA_TYPE_INT INT
PARAM_TAOS_BIGINT TSDB_DATA_TYPE_BIGINT BIGINT
PARAM_TAOS_FLOAT TSDB_DATA_TYPE_FLOAT FLOAT
PARAM_TAOS_DOUBLE TSDB_DATA_TYPE_DOUBLE DOUBLE
PARAM_TAOS_BINARY TSDB_DATA_TYPE_BINARY BINARY
PARAM_TAOS_TIMESTAMP TSDB_DATA_TYPE_TIMESTAMP TIMESTAMP
PARAM_TAOS_NCHAR TSDB_DATA_TYPE_NCHAR NCHAR
PARAM_TAOS_UTINYINT TSDB_DATA_TYPE_UTINYINT UTINYINT
PARAM_TAOS_USMALLINT TSDB_DATA_TYPE_USMALLINT USMALLINT
PARAM_TAOS_UINT TSDB_DATA_TYPE_UINT UINT
PARAM_TAOS_UBIGINT TSDB_DATA_TYPE_UBIGINT UBIGINT

TSDB_DATA_TYPE_INT 等常量,与 PDO 预定义常量的值 部分相等,导致判断数据类型时发生冲突,故人为设定 6000 的差值。例如:

PARAM_TAOS_INT = TSDB_DATA_TYPE_INT + 6000

仅做说明,对开发没有影响。

如果在执行插入操作时,能预先获取数据表的字段类型,再根据绑定的数据的类型,则可以完美兼容,如:

MySQL的字段类型是 INT, SMALLINT, TINYINT, BIGINT,绑定参数为PDO::PARAM_INT 也可以插入 。

但是TDengine 的API在执行 INSERT操作时,不能预先获取数据表的字段类型,只能与数据表的字段完全一致,才可以绑定参数。

3.3 不要混写

PDO::exec 写完整的SQL语句,不要占位符。

PDO::prepare 一定要 bindParam 或者 bindValue,避免 prepare 没有占位符的 SQL,否则会报错。

4. SELECT查询

查询数据分两种方法:

  1. 调用 PDO::query 方法直接执行 SELECT SQL语句。
  2. 调用 PDO::prepare方法执行预处理操作。

4.1 PDO::query方法

<?php $dbh = new PDO("taos:host=127.0.0.1;dbname=demo", "root", "taosdata");

$t1 = strtotime("2022-01-27 16:36:12");
$t2 = strtotime("2022-01-27 16:48:13");

$start_time = intval($t1 * 1000);
$end_time = intval($t2 * 1000);

$sql = "SELECT * FROM device_log_100 WHERE created_timestamp >= $start_time AND created_timestamp query($sql);

$result = $sth->fetchAll(PDO::FETCH_ASSOC);
var_dump($result);
?>

执行结果如下:

array(3) {
  [0]=>
  array(10) {
    ["created_timestamp"]=>
    string(23) "2022-01-27 16:36:12.038"
    ["v_bool"]=>
    string(1) "1"
    ["v_tinyint"]=>
    string(3) "100"
    ["v_smallint"]=>
    string(4) "2000"
    ["v_int"]=>
    string(5) "20000"
    ["v_bigint"]=>
    string(6) "200000"
    ["v_float"]=>
    string(9) "8.08000"
    ["v_double"]=>
    string(11) "8.000008000"
    ["v_binary"]=>
    string(13) "TDengine test"
    ["v_nchar"]=>
    string(9) "Taos test"
  }
  [1]=>
  array(10) {
    ["created_timestamp"]=>
    string(23) "2022-01-27 16:36:49.146"
    ["v_bool"]=>
    string(1) "1"
    ["v_tinyint"]=>
    string(3) "100"
    ["v_smallint"]=>
    string(4) "2000"
    ["v_int"]=>
    string(5) "20000"
    ["v_bigint"]=>
    string(6) "200000"
    ["v_float"]=>
    string(9) "8.08000"
    ["v_double"]=>
    string(11) "8.000008000"
    ["v_binary"]=>
    string(13) "TDengine test"
    ["v_nchar"]=>
    string(9) "Taos test"
  }
  [2]=>
  array(10) {
    ["created_timestamp"]=>
    string(23) "2022-01-27 16:36:55.365"
    ["v_bool"]=>
    string(1) "1"
    ["v_tinyint"]=>
    string(3) "100"
    ["v_smallint"]=>
    string(4) "2000"
    ["v_int"]=>
    string(5) "20000"
    ["v_bigint"]=>
    string(6) "200000"
    ["v_float"]=>
    string(9) "8.08000"
    ["v_double"]=>
    string(11) "8.000008000"
    ["v_binary"]=>
    string(13) "TDengine test"
    ["v_nchar"]=>
    string(9) "Taos test"
  }
}

注意:created_timestamp 在查询时候,用的 时间戳 类型 , 但查询结果格式化时间 类型,这个跟TDengine的客户端查询结果保持一致。

4.2 PDO::prepare方法

<?php $dbh = new PDO("taos:host=127.0.0.1;dbname=demo", "root", "taosdata");

$t1 = strtotime("2022-01-27 16:36:12");
$t2 = strtotime("2022-01-27 16:48:13");

$start_time = intval($t1 * 1000);
$end_time = intval($t2 * 1000);

$sql = "SELECT * FROM device_log_100 WHERE created_timestamp >= :start_time AND created_timestamp prepare($sql);

$sth->bindParam("start_time", $start_time, PDO::PARAM_TAOS_TIMESTAMP);
$sth->bindParam("end_time", $end_time, PDO::PARAM_TAOS_TIMESTAMP);

$sth->execute();
$result = $sth->fetchAll(PDO::FETCH_ASSOC);
var_dump($result);
?>

注意:created_timestamp 在绑定参数时候,用的时间戳类型 , 但查询结果格式化时间 类型,这个跟TDengine的客户端查询结果保持一致。

其他绑定的参数,数据类型也要和数据表的字段类型一致。如果用 PDO::PARAM_INT 或者 PDO::PARAM_STR碰巧运行正常,仅仅是因为运气好而已,不推荐。

谁也想不到,弱类型的PHP,在操作 TDengine 数据库的时候,竟要求 数据类型数据表字段类型 强一致性

5. UPDATE 修改数据

TDengine 默认不支持修改操作。向不支持数据更新的表中写入重复时间戳的数据,后写入的数据会被丢弃

若用户需要数据的更新功能,则在建库的时候,只需要指定数据库的 update 选项为1即可。

例如:

CREATE DATABASE demo UPDATE 1

在向支持数据更新功能的表中,写入重复时间戳的数据时,老数据会被覆盖

6. DELETE 删除数据

截止到现在, TDengine 版本是 V2.4, 数据表暂不支持DELETE操作。

但是数据库支持自动删除,删除时间与 创建数据库时候指定的keep参数有关系,默认3650天,即10年。10年后这个数据库会自动逻辑删除,数据库文件自动备份到适当位置。

其他

PDO 连接 TDengine, 可以不指定数据库。例如:

$dbh = new PDO("taos:host=127.0.0.1", "root", "taosdata");

TDengine的数据库有保存时间,默认10年。

如果设备日志保存为单数据库,3年前的设备日志已没有参考意义,但又不能删除数据,导致占用硬盘空间,同时影响数据库的运行速度。

如果设备日志保存为按 区分的多个数据库。不会导致单数据库和数据集过大,保持了插入和查询数据的高效性。没有意义的设备日志,可以删除、归档。

例如:

<?php try {
    $dbh = new PDO("taos:host=127.0.0.1", "root", "taosdata");

    $year = date("Y");
    $database = sprintf("demo_%s", $year);

    $sql = "INSERT INTO {$database}.device_log_100 (created_timestamp, v_bool, v_tinyint, v_smallint, v_int, v_bigint, v_float, v_double, v_binary, v_nchar ) VALUES (
    ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    $sth = $dbh->prepare($sql);

    $v1 = intval(microtime(true) * 1000);
    $v2 = 1;
    $v3 = 100;
    $v4 = 2000;
    $v5 = 20000;
    $v6 = 200000;
    $v7 = 8.08;
    $v8 = 8.000008;
    $v9 = "TDengine test";
    $v10 = "Taos test";

    $sth->bindParam(1, $v1, PDO::PARAM_TAOS_TIMESTAMP);
    $sth->bindParam(2, $v2, PDO::PARAM_TAOS_BOOL);
    $sth->bindParam(3, $v3, PDO::PARAM_TAOS_TINYINT);
    $sth->bindParam(4, $v4, PDO::PARAM_TAOS_SMALLINT);
    $sth->bindParam(5, $v5, PDO::PARAM_TAOS_INT);
    $sth->bindParam(6, $v6, PDO::PARAM_TAOS_BIGINT);
    $sth->bindParam(7, $v7, PDO::PARAM_TAOS_FLOAT);
    $sth->bindParam(8, $v8, PDO::PARAM_TAOS_DOUBLE);
    $sth->bindParam(9, $v9, PDO::PARAM_TAOS_BINARY);
    $sth->bindParam(10, $v10, PDO::PARAM_TAOS_NCHAR);

    $result = $sth->execute();
    var_dump($result);

    $rowCount = $sth->rowCount()
    var_dump($rowCount);
} catch (Exception $e) {
    printf("%d, %s\n", $e->getCode(), $e->getMessage());
}

?>
Card image cap

每一个你不满意的现在,都有一个你不努力的曾经。