# 基本概念 TDengine有三个模块。一个是客户端模块`taosc`。另外两个是服务端模块,一个是管理节点模块`MGMT`,一个是数据节点模块`DNODE`。客户端模块把请求交给管理节点模块,通过TCP/UDP的方式,在配置文件中通过`mgmtShellPort`来配置端口,默认端口是`6030`。客户端模块也会和数据节点模块打交道,也通过TCP/UDP的方式,默认端口是`6035`。 所有的元数据、存储和查询都会首先和管理节点模块打交道,会有缓存。元数据包括用户、数据库、表。 在数据节点模块中的一个最重要的概念是虚拟节点`vnode`.一个库可以有很多`vnode`。一个表只会在一个`vnode`里,不会跨`vnode`存在。在写数据的时候里面有一个`预写日志`机制,就是把数据先放在缓存里,如果遇到突然断电的情况也没关系,因为当电力恢复之后缓存的数据还会进入硬盘,专业术语叫做`落盘`。落盘有两种方式。一种是`时间驱动落盘`,就是每隔一段时间缓存数据就会进入硬盘,默认为1小时,在`taos.cfg`中的`commitTime`字段配置;另外一种是`数据驱动落盘`,当缓存数据达到一定规模数据会进入硬盘,并且刷新日志文件。关键几个配置如下: ``` 数据保存位置: 默认放在了/var/lib/taos/目录中,通过dataDir设置。 数据分片: 默认vnode中表数据按10天切片,通过daysPerFile设置。 保存时间: 默认是3650天,系统自动删除,通过daysToKeep设置。 数据块: 按列存储。 数据文件种类: -data文件 -head文件 -last文件 ``` **最佳实践** - **一个设备一张表**:提升读取写入速度,在存储介质上是连续的,一张表只有一个写入者,没有锁,以追加的方式写数据 - **采集的物理量**:也就是列,类型包括整型、浮点型、字符串型,没有索引。 - **时间戳**:有索引 - **标签**:保存在超级表的元数据节点中 - **超级表**:方便聚合统计 - **库**:涉及保留时长、备份数、Cache大小、文件块大小、是否压缩分库 - **库和表的数量**:没有限制 # SQL语句 - <> 表示用户需要输入的内容 - [] 表示用户输入的内容可选 - | 表示多选一 - ... 表示前面的项可重复多个 # 时间戳 - 时间格式:YYYY-MM-DD HH:mm:ss.MS, 2017-08-12 18:25:58.128 - 时间分辨率:默认毫秒 - 服务器内部函数:now表示服务器的当前时间 - 时间戳为0:默认使用服务器当前时间 - Epoche Time,长整数:表示从1970-01-01 08:00:00.000开始的毫秒数 - 时间可以加减:比如`now-2h`,a表示毫秒,s表示秒,m表示分钟,h表示小时,d表示天,w表示周,n表示月,y表示年。比如`select * from t1 where ts > now-2w and ts <=now-1w` - 启用微秒:通过`enableMicrosecond` # 数据类型 - TIMESTAMP:8 bytes, 时间戳,最小精度为毫秒,从格林威治时间1970-01-01 08:00:00.000开始 - INT:4 bytes, 整型,范围是[-2^31+1,2^31-1],-2^31被用作null值 - BIGINT: 8 bytes,长整型,范围是[-2^59,2^59] - FLOAT:4 bytes, 浮点型,有效位数6-7 - DOUBLE:8 bytes, 双精度浮点型,有效位数15-16 - BINARY: 长度自定义,记录字符串,最长不能超过504bytes,字符串两端使用单引号,否则英文全部自动转换为小写。使用时需指定大小,比如binary(20)是最长是20个字符的字符串,每个字符站1byte的存储空间,如果字符串超过20个字节,将自动截断。对于字符串内的单引号,用转义符`\'`。 - SMALLINT,2 bytes, 短整型,范围[-32767.32767] - TINYINT, 1 bytes, 单字节整型,范围[-127,127] - BOOL, 1 bytes, 布尔类型,true或false - NCHAR,自定义大小,拥有记录非ASCII字符串,如中文字符,每个nchar占用4个字节大小。字符串两端使用单引号,字符串内的单引号需要转义`\'`,nchar(10)表示最多存储10个nchar字符,占用40bytes空间。如果字符串超出声明长度将被自动截断。 # 数据库管理 SQL语句中的英文字符默认不区分大小写,统一自动转换为小写执行。如果有大小写敏感,需要使用单引号把大写字母引用起来。 - 创建数据库:`CREATE DATABASE MYDB`,默认数据库数据保存10年 - 使用数据库:`USE MYDB` - 删除数据库:`DROP DATABASE MYDB` - 显示所有数据库:`SHOW DATABASES` # 表管理 - 创建表:`CREATE TABLE tb_name (timestamp_field_name TIMESTAMP, field1_name data_type1)`表的第一个字段必须是`TIMESTAMP`类型,系统会自动将其设置为主键。表的每行的长度不能超过托4096个字节。使用数据类型binary或nchar需要制定最长的字节数,比如binary(20) - 删除表:`DROP TABLE tb_name` - 显示数据库下的所有表信息:`SHOW TABLES`,可以使用like过滤,比如`SHOW TABLES LIKE ...`,%匹配0到任意个字符,_表示匹配一个字符 - 获取表结构:`DESCRIBE` - 表增加列:·`ALTER TABLE tb_name ADD COLUMN field_name data_type` - 表删除列:`ALTER TABLE tb_name DROP COLUMN filed_name` 注意: - 如果通过超级表创建,只能针对超级表进行操作,同时针对超级表的结构更改对所有表都会生效。 - 使用`use db_name`操作后操作当前数据库下的表 - 切换到其它数据库的表,可以使用`demo.tb1`来表示 # 数据写入 - 插入一条记录:`INSERT INTO tb_name VALUES (field_value, ...)` - 插入一条记录到指定的列:`INSERT INTO tb_name(field1_name,...) VALUES(field1_value)`,语句中没有出现的列,数据库将自动填充为NULL - 插入多条记录:`INSERT INTO tb_names VALUES (field1_value1, ...) (field1_value2,...)` - 按指定的列插入多条记录:`INSERT INTO tb_name(field1_name,...) VALUES(field1_value1,...)(field1_value2,...)` - 向多个表插入多条记录:`INSERT INTO tb1_name VALUES (field1_value1,...)(field1_value2,...)... tb2_name VALUES (field1_value1,...)(field1_value2,...)...` - 同时向多个表按列插入多条记录 ``` INSERT INTO tb1_name (tb1_field1_name,...) VALUES(field1_value1,...)(field1_value1,...) tb2_name (tb2_field1_name,...) VALUES(field1_value1,...)(field1_value2,...) ``` 注意: - 对于同一张表,记录的时间戳必须是递增,否则会跳过插入该条记录;如果时间戳为0,会默认使用服务器当前时间作为时间戳 - 如果需要将时间戳小于最后一条记录时间的记录写入到数据库,可以使用IMPORT命令替代INSERT命令,语法是一样的;如果同时IMPORT多条记录,需要保证一批记录是按时间戳排序好的 # 数据查询 ``` SELECT {* | expr_list} FROM tb_name [WHERRE where_condition] [ORDER BY _c0 {DESC | ASC}] [LIMIT limit, [, OFFSET offset]] [>> export_file] SELECT function_list FROM tb_name [WHERE where_condition] [LIMIT limit [, OFFSET offset]] [>> export_file] ``` - 返回所有列:* - 指定列 - 对数字列进行四则运算 - 给列取别名 - where语句结合逻辑判断 - where语句结合通配符 - `_c0`表示首列的时间戳 - 输出结果默认按时间戳升序排列 - ORDER BY只能对时间戳操作,对其它字段操作属于非法 过滤操作:同时使用多个字段过滤需要使用`AND`,暂时不支持`OR`。针对同一字段的过滤条件必须单一,例如`Value>20 and value <30`是合法的,`Value<20 AND Value <> 5`是非法的。 - `>` 适用于timestamp 和 numeric types - `<` 适用于timestamp 和 numeric types - `>= ` 适用于 timestamp 和 numeric types - `<=` 适用于 timestamp 和 numeric types - `=` 适用于所有类型 - `<>` 适用于所有类型 - `%` 适用于 binary 和 nchar - `_` 适用于 binary 和 nchar # SQL函数 COUNT ``` SELECT COUNT([*|field_name]) FROM tb_name [WHERE clause] 返回行数或某列的非空值数量 返回类型为INT64 应用于全部字段,表和超级表 ``` AVG ``` SELECT AVG(field_name) FROM tb_name [WHERE clause] 返回统计表或超级表中某列的平均值,返回结果类型为double,不能应用在timestamp, binary, nchar, bool字段 ``` WAVG ``` SELECT WAVG(field_name) FROM tb_name WHERE clause 加权平均,返回sdouble,不能用在timestamp, binary, nchar, bool类型,适用于表和超级表 ``` SUM ``` SELECT SUM(field_name) FROM tb_name [WHERE clause] 统计表或超级表某列的和,返回double或int64,不能用在timestamp, binary, nchar, bool ``` STDDEV ``` SELECT STDDEV(field_name) FROM tb_name [WHERE clause] 统计某列的均方差,返回double,不能用在timestamp, binary, nchar, bool ``` LEASTSQUARES ``` SELECT LEASTSQUARES(field_name) FROM tb_name [WHERE clause] 返回表中某列值是主键(时间戳)的拟合直线方程,返回结果是字符串(斜率,截距),不能应用在timestamp, binary, nchar, bool,自变量是时间戳,因变量是该列的值,适用于表 ``` MIN ``` SELECT MIN(field_name) FROM {tb_name | stb_name} [WHERE clause] 不能用在timestamp, binary, ncahr, bool ``` MAX ``` SELECT MAX(field_name) FROM {tb_name | stb_name} [WHERE clause] ``` FIRST ``` SELECT FIRST(field_name) FROM {tb_name | stb_name} [WHERE clause] 表或超级表中某列值最先写入的非NULL值,适用于所有字段,各个列FIRST(*),如果结果集中的某列全部为NULL,该列的返回结果也是NULL,如果结果集中所有列全部为NULL值则不返回结果 ``` LAST ``` SELECT LAST(field_name) FROM {tb_name | stb_name} [WHERE clause] 表或超级表中某列的值最后写入的非NULL值,所有字段,如果返回各个列LAST(*),如果结果集中某列全部为NULL值则该列返回的结果也是NULL,如果结果集中所有列全部为NULL则不返回结果。 ``` TOP ``` SELECT TOP(field_name, K) FROM {tb_name | stb_name} [WHERE clause] 统计表或超级表中某列的值最大k个非null值,不能用在timestamp, binary, ncahr, bool,k的取值范围在[1,100] ``` BOTTOM ``` SELECT BOTTOM(field_name, K) FROM {tb_name | stb_name} [WHERE clause] ``` PERCENTILE ``` SELECT PERCENTILE(field_name, P) FROM {tb_name | stb_name} [WHERE clause] 表中某列值的百分比分位数,返回double, 不能用在timestamp, binary, ncahr, bool,k的范围0和100之间,0时候等同于MIN,100的时候等同于MAX ``` LAST_ROW ``` SELECT LAST_ROW(field_name) FROM {tb_name | stb_name} 返回表或超级表的最后一条记录,与last函数不同,last_row不支持时间范围限制,强制返回最后一条记录 ``` DIFF ``` SELECT DIFF(field_name) FROM tb_name [WHERE clause] 统计表中某列的值与前一行对应值的差,不能应用在timestamp, binary, nchar, bool类型 ``` SPREAD ``` SELECT SPREAD(field_namne) FROM {tb_name | stb_name}[WHERE clause] 统计超级表或表中某列的最大值和最小值之差,返回double,不能用在binary, nchar, bool类型,可用于TIEMSTAMP字段表示记录的时间覆盖范围 ``` 四则运算 ``` SELECT field_name [+|-|*|/|%][value | field_name] from {tb_name | stb_name} [WHERE clause] 统计表或超级表中某列或多列的计算,不能用在timestamp, binary, nchar, bool类型,支持两列或多列之间计算,使用括号表示优先级,Null字段不参与计算,如果参与计算的某行中保包含null,改行的结果是Null ``` # 举例 ``` CREATE TABLE tb1(ts timestamp, col1 int, col2 float, col3 binary(50)) SELECT * FROM tb1 WHERE ts >= NOW - 1h SELECT * FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND ts <= '2018-06-02 08:00:00.000' AND col3 LIKE `%nny` ORDER BY ts DESC SELECT (col1 + col2) AS `complex` FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND col2 > 1.2 LIMIT 10 OFFSET 5 SELECT COUNT(*) FROM tb1 WHERE ts >= NOW - 10m AND col2 > 3.14 >> /home/testoutpu.csv ``` # 时间维度聚合 按照时间切割再聚合,比如温度传感器每秒采集一次数据,徐查询每隔10分钟温度的平均值。 ``` SELECT function_list FROM tb_name [WHERE where_condition] INTERVAL(interval) [FILL ({None | VALUE | PREV | NULL | LINEAR})] SELECT function_list FROM stb_name [WHERE where_condition] [GTOUP BY tags] [FILL ({VALUE | PREV | NULL | LINEAR})] ``` - 聚合时间段由INTERVAL指定,最短时间10毫秒,10a - 仅限于单个输出的函数:count, avg, sum, stddev, leastsquares, percentile, min, max, first, last不能用于多行输出函数比如top, bottom, diff和四则运算 - WHERE语句指定查询的起止时间和过滤条件 - FILL语句指定某一时间数据确实情况下的填充模式。NONE,也是默认的方式,不填充;VALUE,固定值填充;NULL,使用NULL值填充;PREV,使用上一个非NULL值填充数据 - 使用FILL时候,务必指定查询的时间区间,针对每次查询系统可返回不超过1千万条结果 - 在实践维度聚合中,返回的结果中时间序列严格单调递增 - 如果查询对象是超级表,则聚合函数会作用于该超级表下满足值过滤条件的所有表。如果查询中没有group by语句,则返回的结果按照该时间序列严格单调递增。如果查询中使用了group by语句分组,则返回结果中每个group内部按照时间序列严格单调递增。 ``` create table sensor(ts timestamp, degree double, pm25 smallint) SLECT AVG(degree), MAX(degree), LEASTSQUARES(degree), PERCENTILE(degree, 50) FROM sensor WHERE TS>=now -1d //过去20小时 INTERVAL(10M) //以10分钟为一个阶段 FILL(PREV); ``` # 超级表STable 对单个采集点单独建表导致表数量增加,如何对这些表进行聚合、统计呢?引入STable。 > 什么是超级表STable - 同类型数据采集点的抽象 - 包含多长数据结构一样的字表 - STable为其子表定义了表结构和一组标签。表结构就是表中记录的数据列及其数据类型。标签名也由STable定义,标签值记录每个子表的静态信息,用于对子表进行分组过滤。 - 子表本质上就是普通的表,由一个时间戳和若干数据列组成 - 子表与普通表的区别在于每个子表从属于一张超级表,并带有一组STable定义的标签值 - 每种类型的采集设备可以定义一个STable。比如定义表的每列的数据类型,比如温度、电压、电流。标签信息属于Meta Data,如采集设别额序列号、型号、位置等是静态的。用户在创建表时指定Stable,还可以指定标签的值,也可以事后增加或修改。 ``` create table thermometer (ts timestamp, degree float) tags (location binary(20), type int) create table t1 using thermometer tags ('beijing', 10) ``` - 可以使用STable创建数量无上限的、具有不同标签的表 - STable就是具有相同数据模型,不同标签的表的集合 - 将标签数据和采集的动态数据完全分离,对标签建立了高性能内存索引结构 - 一个库可以有多个STable, 一个STable有多个子表 > 超级表管理 - 创建超级表:`CREATE TABLE ( TIMESTAMP, field_name1 field_type,...) TAGS (tag_name tag_type)`,TAGS的总长度不能超过512字节,数据类型不能是timestamp和nchar,不能与其它列名相同,不能为预留关键字 - 显示已创建的超级表:`show stables;` - 删除超级表:`DROP TABLE `,删除超级表的前提要求先删除所有的子表 - 查看属于某个超级表并满足条件的子表:`SELECT TBNAME,[TAG_NAME,…] FROM WHERE <[=|=<|>=|<>] values..> ([AND|OR] …)` - 查看属于某个超级表并满足查询条件的表,TBNAME为关键字:`SELECT COUNT(TBNAME) FROM WHERE <[=|=<|>=|<>] values..> ([AND|OR] …)` > 写数据时自动创建子表 当定义好了超级表,在写数据的时候并不知道某个设备是否存在,可以采用自动创建子表,需要注意的是超级表是不能自动创建的。 ``` INSERT INTO USING TAGS (,...) VALUES (field_value,...)(field_value,...)... ``` 对多个超级表下的子表创建。 ``` INSERT INTO USING TAGS (, ...) VALUES (,...)(,...)... USING TAGS(,...) VALUES (,...) ``` > TAG管理 - 添加新的标签:`ALTER TABLE ADD TAG `,标签总数不能超过6个 - 删除标签:`ALTER TABLE DROP TAG `,第一列标签不能删除,至少保留一个标签,从超级表删除某个标签后,其下的所有子表也会自动删除标签 - 修改标签名:`ALTER TABLE CHANGE TAG `,其下所有子表的标签名也会改变 - 修改子表的标签值:`ALTER TABLE SET TAG =` > 多表聚合 通过TAG值进行条件过滤,暂不支持针对binary类型的模糊匹配过滤。 ``` SELECT function,… FROM WHERE <[=|<=|>=|<>] values..> ([AND|OR] …) INTERVAL (