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 聚合查询
- 非时间窗口类:用户可见层面无变化。
- 时间窗口聚合类:TDengine 系统中针对时间窗口采用闭区间进行描述,并且相同时间戳不同primary key的记录归属于同一个时间窗口。故用户可见的时间窗口无变化。
- 在超级表的查询中,已经有归属于不同时间线的相同时间戳的查询,增加了 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 接口变更
-
tmq_get_json_meta(获取meta信息的 json 描述)
该接口内部创建表数据结构里增加列是否是复合主键参数("isPrimarykey":true)。已在文档里更新: https://jira.taosdata.com:18090/pages/viewpage.action?pageId=158206215
-
tmq_get_raw 接口返回的 raw data 做了升级,请查看文档 数据订阅结果序列化方案
2.6.2 其它接口无变更
2.7 Last 缓存
-
cache_model: none,返回最大的时间戳和该时间戳下最大的主键所对应的行或值
-
cache_mode: last_value, last_row, both,查询行为受缓存更新规则影响,更新规则如下:
缓存的更新粒度仍然是表级别。在主时间戳列相同情况下,主键列按大小顺序取最大值。即在更新缓存时,新到达的数据与当前 last 缓存中的数据相比,如果时间戳更大或时间戳相同但主键值更大则更新缓存,否则不更新缓存。
3. 性能影响
3.1 写入性能
-
非重复时间戳:在没有任何两条记录时间戳相同的情况下,其写入性能与未引入 primary key 相比完全相同
-
有重复时间戳的场景:在极端情况下,时间戳全部相同,而 primary key 单调递增,预估这种场景下写入性能下降 50%,原因是要进行两次键值比较。
-
纯乱序数据写入:即任意一行记录的时间戳和 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. 约束和限制
-
只允许额外指定除时间戳以外的一列作为主键
-
支持定长和变长类数据类型,暂定支持非浮点数及 varchar。
-
主键与时间戳列一样,不允许缺省或为 NULL
-
主键列不允许 alter 或 drop 等操作
-
已建无主键列的表不可以通过 alter 语句添加主键列
-
不支持对主键列的范围删除
6. 总结
本章主要介绍了新功能复合主键的对外接口使用、影响的功能及函数内部技术实现,受约条件等,帮助技术开发人员更好了解及使用此功能。
相关文章:

TDengine 新功能 复合主键
1. 简介 从 TDengine 3.3.0.0 版本之后,新增了复合主键的功能。 TDengine 原来的时间列是不允许有重复时间戳的,有了复合主键功能后,时间列即允许有重复,重复后的时间戳按紧跟其后第二列主键列的值来确定唯一性。 此功能的常用…...

JVM 面试题
Java 虚拟机(JVM)是运行 Java 程序的引擎,它是 Java 语言 “一次编译,处处运行” 的核心技术。JVM 的主要任务是将 Java 字节码(Bytecode)解释成机器码并执行,负责内存管理、线程管理、垃圾回收…...

组件上传图片不回显问题
import { Plus } from "element-plus/icons-vue"; // 图片上传 const img_add ref([]); function httpRequest_add(option) {let dataForm new FormData();dataForm.append("file", option.file);dataForm.append("id", user.data.id);axios({…...

【JavaWeb后端学习笔记】Spring AOP面向切面编程
AOP 1、Spring AOP概述2、SpringAOP快速入门3、SpringAOP核心概念4、通知类型5、通知顺序6、切入点表达式6.1 execution方式6.2 annotation方式 7、连接点 1、Spring AOP概述 AOP:Aspect Oriented Programming,面向特定方法编程。 AOP是通过动态代理技术…...

6.584-Lab5B
6.584-Lab5B Reference CodeReference BlogHomeworkMyself Code Sharded Key/Value Service 梗概 这个图是我从上面参考blog中拿来的,觉得做的不错,借助这张图来讲解一下需要一个什么样的 Service。 ShardCtrler Client: 接收来自客户发出的命…...

OceanBase 的探索与实践
作者:来自 vivo 互联网数据库团队- Xu Shaohui 本文总结了目前我们遇到的痛点问题并通过 OceanBase 的技术方案解决了这些痛点问题,完整的描述了 OceanBase 的实施落地,通过迁移到 OceanBase 实践案例中遇到的问题与解决方案让大家能更好的了…...

安卓调试环境搭建
前言 前段时间电脑重装了系统,最近准备调试一个apk,没想到装环境的过程并不顺利,很让人火大,于是记录一下。 反编译工具下载 下载apktool.bat和apktool.jar 官网地址:https://ibotpeaches.github.io/Apktool/install…...

动画Lottie
Lottie简介 Lottie是一个Airbnb 开发的用于Android,iOS,Web和Windows的库,用于解析使用Bodymovin导出为json的Adobe After Effects动画,并在移动设备和网络上呈现 — GitHub Lottie主要特性 After Effects 兼容性: …...

C++感受14-Hello Object 封装版 - 上
1. 封装即约束——封装和派生、多态的本质区别 一门计算机语言,要如何帮助程序员写出优秀的代码?两个方法:一是给程序员更多能力,二是给程序员更多约束。之前我们学习的派生和多态,更多的是给我们技能,而封…...

网络安全中大数据和人工智能应用实践
传统的网络安全防护手段主要是通过单点的网络安全设备,随着网络攻击的方式和手段不断的变化,大数据和人工智能技术也在最近十年飞速地发展,网络安全防护也逐渐开始拥抱大数据和人工智能。传统的安全设备和防护手段容易形成数据孤岛࿰…...

RISC-V架构下OP-TEE 安全系统实践
安全之安全(security)博客目录导读 本篇博客,我们聚焦RISC-V 2024中国峰会上的RISC-V和OP-TEE结合的一个安全系统实践,来自芯来科技桂兵老师。 关于RISC-V TEE(可信执行环境)的相关方案,如感兴趣可参考R...

40分钟学 Go 语言高并发:【实战】分布式缓存系统
【实战课程】分布式缓存系统 一、整体架构设计 首先,让我们通过架构图了解分布式缓存系统的整体设计: 核心组件 组件名称功能描述技术选型负载均衡层请求分发、节点选择一致性哈希缓存节点数据存储、过期处理内存存储 持久化同步机制节点间数据同步…...

[创业之路-186]:《华为战略管理法-DSTE实战体系》-1-为什么UTStarcom死了,华为却活了,而且越活越好?
目录 前言 一、市场定位与战略选择 二、技术创新能力 三、企业文化与团队建设 四、应对危机的能力 五、客户为中心的理念 六、市场适应性与战略灵活性 七、技术创新与研发投入 八、企业文化与团队建设 九、应对危机的能力 前言 UT斯达康(UTStarcom&#…...

python如何多行注释
在Python中,多行注释通常有两种方式: 使用三个单引号()或三个双引号(""")来创建多行字符串,这可以被用来作为多行注释。这种方式在Python中实际上是创建了一个多行的字符串对象…...

前端工程化面试题目常见
前端工程化面试常见题目包括: • 谈谈你对WebPack的认识。 • Webpack打包的流程是什么? • 说说你工作中几个常用的loader。 • 说说HtmlWebpackPlugin插件的作用。 • Webpack支持的脚本模块规范有哪些? • Webpack和gulp/grunt相比有什么特…...

定点数的乘除运算
原码一位乘法 乘积的符号由两个数的符号位异或而成。(不参与运算)被乘数和乘数均取绝对值参与运算,看作无符号数。乘数的最低位为Yn: 若Yn1,则部分积加上被乘数|x|,然后逻辑右移一位;若Yn0&…...

页面置换算法模拟 最近最久未使用(LRU)算法
最近最久未使用(LRU)算法是一种基于页面访问历史的页面置换算法。它选择最久未使用的页面进行置换。当需要访问一个不在内存中的页面时,如果内存已满,则选择最久未使用的页面进行置换。LRU算法通过记录页面的访问时间戳来判断页面…...

Ubuntu与Centos系统有何区别?
Ubuntu和CentOS都是基于Linux内核的操作系统,但它们在设计理念、使用场景和技术实现上有显著的区别。以下是详细的对比: 1. 基础和发行版本 Ubuntu: 基于Debian,使用.deb包管理系统。包含两个主要版本: LTSÿ…...

RK3568平台开发系列讲解(pinctrl 子系统篇)pinctrl_debug
🚀返回专栏总目录 文章目录 1. Overview2. debug信息2.1 pinctrl-devices2.2. pinctrl-handles2.3. pinctrl-handles3. debug信息3.1. 查看(pinctrl_register_pins)注册了哪些pins3.2. 查看pin groups;3.3. 查看每种functions所占用的gpio groups信息:3.4. pinconf沉淀、…...

避大坑!Vue3中reactive丢失响应式的问题
在vue3中,我们定义响应式数据无非是ref和reactive。 但是有的小伙伴会踩雷!导致定义的响应式丢失的问题。 reactive丢失响应式的情况1(直接赋值) 场景: 1.你定义了一个数据:let datareactive({name:"",age:"" }) 2.然后你…...

springSecurity权限控制
权限控制:不同的用户可以使用不同的功能。 我们不能在前端判断用户权限来控制显示哪些按钮,因为这样,有人会获取该功能对应的接口,就不需要通过前端,直接发送请求实现功能了。所以需要在后端进行权限判断。࿰…...

Pytorch训练固定随机种子(单卡场景和分布式训练场景)
模型的训练是一个随机过程,固定随机种子可以帮助我们复现实验结果。 接下来介绍一个模型训练过程中固定随机种子的代码,并对每条语句的作用都会进行解释。 def seed_reproducer(seed2333):random.seed(seed)os.environ["PYTHONHASHSEED"] s…...

Conda + JuiceFS :增强 AI 开发环境共享能力
Conda 是当前 AI 应用开发领域中非常流行的环境和包管理系统,因其能够简单便捷地创建与系统资源相隔离的虚拟环境广受欢迎。 Conda 支持在不同的操作系统上重建相同的工作环境,但在环境共享复用方面仍存在一些挑战。比如,在不同机器上复用相…...

人工智能-人机交互的机会
目录 引言HCI领域的发展机会人工智能领域的崛起与机会博雅智信的HCI与AI辅导服务结语 引言 在人类科技不断进步的今天,HCI(人机交互)和人工智能(AI)是两个密切相关且充满潜力的领域。HCI研究如何优化人类与计算机之间…...

【系统架构核心服务设计】使用 Redis ZSET 实现排行榜服务
目录 一、排行榜的应用场景 二、排行榜技术的特点 三、使用Redis ZSET实现排行榜 3.1 引入依赖 3.2 配置Redis连接 3.3 创建实体类(可选) 3.4 编写 Redis 操作服务层 3.5 编写控制器层 3.6 测试 3.6.1 测试 addMovieScore 接口 3.6.2 测试 g…...

elasticsearch基础总结
最近实习,项目用的elasticseatch做的存储库,但是之前对于es接触的不多,查询语法有些不熟,每次想写个DSL查询时都要gpt或者施展搜索大法,所以索性就自己总结总结,以后忘了也方便查。所以这篇文章会持续更新。…...

【慕伏白教程】Zerotier 连接与简单配置
文章目录 下载与安装WindowsLinuxapt安装官方脚本安装 Zerotier 配置新建网络网络配置 终端配置WindowsLinux 下载与安装 Windows 进入Zerotier官方下载网站,点击下载 在下载目录找到安装文件,双击打开后点击 Install 开始安装 安装完成后,…...

Brain.js(九):LSTMTimeStep 实战教程 - 未来短期内的股市指数预测 - 实操要谨慎
系列的前一文RNNTimeStep 实战教程 - 股票价格预测 讲述了如何使用RNN时间序列预测实时的股价, 在这一节中,我们将深入学习如何利用 JavaScript 在浏览器环境下使用 LSTMTimeStep 进行股市指数的短期预测。通过本次实战教程,你将了解到如何用…...

C# 字符串(String)
文章目录 前言创建 String 对象的方式1. 通过给 String 变量指定一个字符串2. 通过使用 String 类构造函数3. 通过使用字符串串联运算符( )4. 通过检索属性或调用一个返回字符串的方法5. 通过格式化方法来转换一个值或对象为它的字符串表示形式 String …...

二进制文件
大多数人听到“二进制”的时候,脑海里可能马上就会联想到电影《黑客帝国》中由“0”和“1”组成的矩阵。 笔者不打算在这里详细讨论二进制的运算、反码、补码之类枯燥的东西,但有几个和开发相关的概念需要做一点澄清和普及。因为这些内容就像空气——用…...