自学内容网 自学内容网

TDengine 新功能 复合主键

1. 简介

从 TDengine 3.3.0.0 版本之后,新增了复合主键的功能。

TDengine 原来的时间列是不允许有重复时间戳的,有了复合主键功能后,时间列即允许有重复,重复后的时间戳按紧跟其后第二列主键列的值来确定唯一性。

此功能的常用场景如高速收费口,同一时间内可能会多辆车通过,股票交易记录,同一时间内笔成交等多种实际应用场景。

2. 功能说明

2.1 建表

用户可以在建表时用 PRIMARY KEY 关键字,额外指定除主键时间戳列以外的另一列作为主键列,该列与时间戳列共同组成一行数据的键值,其类型必须为整型(int32, int64, uint32, uint64) 或 字符串类型(varchar)。超级表与普通表均支持复合主键。复合主键列不支持修改、增加和删除操作。PRIMARY KEY 列只能设置为第二列

具体 SQL 语句如下:

CREATE TABLE t ( ts TIMESTAMP, obj_id VARCHAR(64) PRIMARY KEY, data1 FLOAT, data2 int );

2.2 写入

只有当时间戳相同且 PRIAMRY KEY 列值都相同时,两行数据才会被认为是重复数据,否则被认为是两行不同数据。如果时间戳相同但 Primary key 较小数据后写入,则视为乱序数据处理

schemaless 写入不支持复合主键的情况,因为 schemaless 本身就是没有 schema,没法确定复合主键,目前的三种 schemaless 协议 (influxdb line/opentsdb telnet/opentsdb json) 也没有复合主键这个概念。

2.3 删除

删除操作与现有删除操作一样,只支持按时间段删除,不支持按主键范围删除

2.4 查询

2.4.1 数据读取

查询复合主键数据时,时间戳相同且 PRIMARY KEY 列值相同时,被认为是一行数据。对于复合主键(时间戳+PRIMARY KEY 列)相同但被多次写入的重复数据则在合并更新后返回。只有复合主键不同的行被认为不同的行数据,被查询分别返回。每个表的读取结果是按(时间戳主键,Primary Key)有序。其他查询行为也将相应地变动。具体变化内容见后续章节。

查询行为的适配源自于TSDB reader 返回给上层的基础数据结构 SSDataBlock中包含了时间戳相同,但primary key 不同的记录。

2.4.2 标量查询

此原来行为相同,无改变

2.4.3 聚合查询

  1. 非时间窗口类:用户可见层面无变化。
  2. 时间窗口聚合类:TDengine 系统中针对时间窗口采用闭区间进行描述,并且相同时间戳不同primary key的记录归属于同一个时间窗口。故用户可见的时间窗口无变化。
  3. 在超级表的查询中,已经有归属于不同时间线的相同时间戳的查询,增加了 primary key 以后,用户可见表现层面无任何变化。

2.4.4 查询函数

类别函数名是否变化
数据函数ABS无影响
ACOS无影响
ASIN无影响
ATAN无影响
CEIL无影响
COS无影响
FLOOR无影响
LOG无影响
POW无影响
ROUND无影响
SIN无影响
SQRT无影响
TAN无影响
字符串函数CHAR_LENGTH无影响
CONCAT无影响
CONCAT_WS无影响
LENGTH无影响
LOWER无影响
LTRIM无影响
RTRIM无影响
SUBSTR无影响
UPPER无影响
时间和日期函数TIMEDIFF无影响
TIMETRUNCATE无影响
转换函数CAST无影响
TO_ISO8601无影响
TO_JSON无影响
TO_UNIXTIMESTAMP无影响
TO_CHAR无影响
TO_TIMESTAMP无影响
聚合函数APERCENTILE无影响
AVG无影响
COUNT无影响
ELAPSED无影响
LEASTSQUARES无影响
SPREAD无影响
STDDEV无影响
SUM无影响
HYPERLOGLOG无影响
HISTOGRAM无影响
PERCENTILE无影响
选择函数BOTTOM无影响
FIRST有变化
INTERP有变化
LAST有变化
LAST_ROW有变化
MAX无影响
MIN无影响
MODE无影响
SAMPLE无影响
TAIL无影响
TOP无影响
UNIQUE有变化
时序数据特有函数CSUM无影响
DERIVATIVE有变化
DIFF有变化
IRATE有变化
MAVG无影响
STATECOUNT无影响
STATEDURATION无影响
TWA有变化

变化的函数行为变化如下:

1. Interp

Interp 函数返回设定时间点 T 0 的插值(断面)数据,其前置时间戳 Tprev(小于 T0的最大时间戳)或后置时间戳Tnxt(大于T0的最小时间戳)可能有重复时间,并且其对应的数值不同。

进行插值计算时,前置时间戳 Tprev 或后置时间戳 Tnxt 均只使用首次出现的记录进行计算,丢弃其后出现的相同时间戳不同主键的记录。

首次出现的定义:对于任一时间戳Tx,在按照升序返回的数据记录中,(同一个表中数据)第一条 Tx 的记录即为首次出现记录。降序返回的数据记录中(同一个表中数据)Tx 最后一次出现的记录为首次出现。

需要注意,首次出现的判定只针对同一个表,对于超级表下的不同的表,无首次出现的判定

2. FIRST

First 返回时间戳最小的记录。对于同一个表中具有相同(最小)时间戳不同主键的情况,返回该表中具有该时间戳的所有记录中首次出现的记录。

首次出现定义同上。

超级表查询中,在完成上述逻辑以后,还需要对来自不同表的时间戳进行比较和取舍,针对来自不同vnode的记录,如果 ts 相同需要进行 primary key 的比较。因此 first 函数返回的全局最小的(ts + primary key) 最早的结果。

3. LAST

Last 返回时间戳最大的记录。对于同一个表中具有相同(最大)时间戳不同主键的情况,返回该表中具有该时间戳的所有记录中末次出现的记录。

末次出现定义对应于首次出现的定义。

针对超级表跨 vnode 查询返回的结果,其判定逻辑于 FIRST 函数处理逻辑相同,需要同时比较 ts + primary key

4. LAST_ROW

与 last 函数相同。

5. UNIQUE

Unique 返回任一时间戳第一次出现的记录。对于同一个表中的记录,只返回首次出现的记录。不同子表中的相同时间戳记录,判定逻辑不变。

首次出现定义同上。

6. DERIVATIVE

同一个子表中的记录,使用首次出现记录进行计算,忽略后续出现的相同时间戳不同主键的记录。

首次出现定义同上。

7. DIFF

同一个子表中的记录,使用首次出现记录进行计算,忽略后续出现的相同时间戳不同主键的记录。

首次出现定义同上。

8. IRATE

同 derivative 函数处理逻辑相同。

9. TWA

同一个子表中数据进行窗口边界插值的时候,均使用首次出现记录进行插值计算返回结果。非首次出现记录不参与计算。

首次出现定义同上。

10. show create

Show create 返回创建(超级)表的SQL 语句,对于有 primary key 的(超级)表,相应的 SQL 语句有变化

11. DESC <table_name>

DESC 获取 (超级)表的 schema 信息,返回的结果中,需要标记第二列(primary key 只能为第二列)是否是 primary key 列。

12. INSERT INTO <table_name> SELECT

会进行符合性检查,具有 primary key 的(超级)表的不能向无 primary key 的表中写入数据。无 primary key 的表可以向具有 primary key 的表写入数据。

向 primary key 的表写入数据的时候,要求 primary key 列必须非 NULL。

2.4.5 查询子句

序号

子句

行为变化

1

比较表达式/条件子句

无影响

2

fill 子句

无影响

3

窗口子句

无影响

4

group by 子句

无影响

5

partition by 子句

无影响

6

join 查询

无影响

7

Distinct 子句

无影响

8

union子句

无影响

9

slimit子句

无影响

10

limit子句

无影响

11

order by子句

无影响

12

having子句

无影响

13

range子句

无影响

14

every子句

无影响

15

子查询

无影响

2.5 流计算

2.5.1 流计算窗口

对我们所支持的窗口类型,即 Interval、Session窗口,如果数据源表是复合主键,数据先按时间戳排序,时间戳相同的,再按复合主键排序,然后才是其他列排序。所以复合主键场景,流计算当前的设计和实现能够透明处理,符合流计算窗口当前的预期。存储流计算结果的表是复合主键,则Interval、Session 窗口输出结果时,需要按照时间戳和复合主键排序。

流计算对于乱序数据的界定方式:当前这批写入的数据,与之前写入的数据做对比。窗口计算要求数据按时间戳有序,只是要求当前这批数据,这个是局部的,并不是要求数据源的全部数据有序。

2.5.2 创建流计算

需要考虑存储流计算结果的表。对于自动创建超级表场景,需要提供复合主键的信息。新增语法,在创建流计算的SQL中,显式指定复合主键信息,即流计算结果中,哪些列是复合主键。对于写入已存在超级表,通过元数据能够获取复合主键的信息,不需要SQL中指定。

语法如下:

CREATE STREAM [IF NOT EXISTS] stream_name [stream_options] INTO stb_name[(field1_name, field2_name [PRIMARY KEY], field3_name, ...)] [TAGS (create_definition [, create_definition] ...)] SUBTABLE(expression) AS subquery

PRIMARY KEY 使用规则和限制与建表SQL相同。

2.5.3 流计算中不支持的场景

由于需要按照 PK 删除 已经生成的记录。因此,不支持状态窗口、事件窗口、计数窗口在有 pk 的表上进行计算。对于全是标量函数的流计算,不支持数据源是复合主键,且目标表不是复合主键。

2.6 订阅

2.6.1 接口变更

  1. tmq_get_json_meta(获取meta信息的 json 描述)

        该接口内部创建表数据结构里增加列是否是复合主键参数("isPrimarykey":true)。已在文档里更新: https://jira.taosdata.com:18090/pages/viewpage.action?pageId=158206215

  2. tmq_get_raw 接口返回的 raw data 做了升级,请查看文档 数据订阅结果序列化方案

2.6.2 其它接口无变更

2.7 Last 缓存

  1. cache_model: none,返回最大的时间戳和该时间戳下最大的主键所对应的行或值

  2. cache_mode: last_value, last_row, both,查询行为受缓存更新规则影响,更新规则如下:

     缓存的更新粒度仍然是表级别。在主时间戳列相同情况下,主键列按大小顺序取最大值。即在更新缓存时,新到达的数据与当前 last 缓存中的数据相比,如果时间戳更大或时间戳相同但主键值更大则更新缓存,否则不更新缓存。

3. 性能影响

3.1 写入性能

  1. 非重复时间戳:在没有任何两条记录时间戳相同的情况下,其写入性能与未引入 primary key 相比完全相同

  2. 有重复时间戳的场景:在极端情况下,时间戳全部相同,而 primary key 单调递增,预估这种场景下写入性能下降 50%,原因是要进行两次键值比较。

  3. 纯乱序数据写入:即任意一行记录的时间戳和 primary key 都有可能小于之前已经写入的部分记录,这种情况下仍旧无法预估性能下降的比例。原则乱序程度越高,性能下降越明显

3.2 查询性能

3.2.1 数据读取

读取过程中,在merge阶段需要同时比较 ts 和 primary key column,因此 merge 过程会消耗更多的 CPU,记录合并过程会出现明显地性能下降。

合并过程是在 tsdb 完成,因此所有的查询均受影响(只使用 head 文件 和 SMA 索引的查询除外)。性能受到的影响程度受 key长度、数据写入模式、数据在文件中物理分布、数据重复比例、数据读取开销在整个查询过程中资源开销占比等诸多因素共同影响,无法简单评估性能下降的程度。

3.2.2 不同类型查询性能表现

在排除数据读取性能降低的场景下,对于非分组类型的查询(没有 partition by 和 group by 子句),没有明显的变化。

4. 兼容性

1. 不会对已存在的数据产生影响,数据不支持版本回退,因为低版本识别不出复合主键。

2. 流计算会有影响。需要删除删除流计算才能进行升级。

5. 约束和限制

  1. 只允许额外指定除时间戳以外的一列作为主键

  2. 支持定长和变长类数据类型,暂定支持非浮点数及 varchar。

  3. 主键与时间戳列一样,不允许缺省或为 NULL

  4. 主键列不允许 alter 或 drop 等操作

  5. 已建无主键列的表不可以通过 alter 语句添加主键列

  6. 不支持对主键列的范围删除

6. 总结

本章主要介绍了新功能复合主键的对外接口使用、影响的功能及函数内部技术实现,受约条件等,帮助技术开发人员更好了解及使用此功能。


原文地址:https://blog.csdn.net/ticktick999/article/details/144356981

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!