小 T 导读:为了在数据采集项频繁变动的情况下保证用户仍然能够顺利地完成数据记录工作,TDengine 提供了三种无模式写入协议,分别是 InfluxDB Line 协议、OpenTSDB Telnet 协议和 OpenTSDB JSON 格式协议。本文将对无模式写入方式的主要处理逻辑、映射规则与变更处理等进行分析,便于用户理解与使用。

通常来说,物联网应用常会采集比较多的数据项,用于实现智能控制、业务分析、设备监控等功能。但在此过程中,由于应用逻辑的版本升级,或者设备自身的硬件调整等原因,数据采集项可能较为频繁地出现变动。这也是时序数据库(Time Series Database,TSDB)需要应对的一个挑战。

为了在这种情况下顺利完成数据记录工作,TDengine 提供了 Schemaless 写入方式,让用户可以省略掉预先创建超级表/子表的步骤,凭借数据写入接口能够自动创建与数据对应的存储结构。在必要时,Schemaless 将自动增加必要的数据列,保证用户写入的数据能够被正确存储。

值得一提的是,通过无模式写入方式建立的超级表及其对应的子表,与通过 SQL 直接建立的超级表和子表完全没有区别,你也可以通过 SQL 语句直接向其中写入数据。但需要注意,通过无模式写入方式建立的表,其表名是基于标签值按照固定的映射规则生成的,所以无法明确地进行表意,缺乏可读性。

无模式写入行协议

TDengine 的无模式写入行协议兼容 InfluxDB 的行协议(Line Protocol)、OpenTSDB 的 telnet 行协议、OpenTSDB 的 JSON 格式协议。但是使用这三种协议的时候,需要在 API 中指定输入内容使用解析协议的标准。

对于 InfluxDB、OpenTSDB 的标准写入协议请参考《在 TDengine 中如何高效写入?四种写入方式提效大全》。下面首先以 InfluxDB 的行协议为基础,介绍 TDengine 扩展的协议内容,允许用户采用更加精细的方式控制(超级表)模式。

Schemaless 采用一个字符串来表达一个数据行(可以向写入 API 中一次传入多行字符串来实现多个数据行的批量写入),其格式约定如下:

measurement,tag_set field_set timestamp

其中:

  • measurement 将作为数据表名。它与 tag_set 之间使用一个英文逗号来分隔。
  • tag_set 将作为标签数据,其格式形如 <tag_key>=<tag_value>,<tag_key>=<tag_value>,也即可以使用英文逗号来分隔多个标签数据。它与 field_set 之间使用一个半角空格来分隔。
  • field_set 将作为普通列数据,其格式形如 <field_key>=<field_value>,<field_key>=<field_value>,同样是使用英文逗号来分隔多个普通列的数据。它与 timestamp 之间使用一个半角空格来分隔。
  • timestamp 即本行数据对应的主键时间戳。

tag_set 中所有的数据自动转化为 nchar 数据类型,并不需要使用双引号(”)。在无模式写入数据行协议中,field_set 中的每个数据项都需要对自身的数据类型进行描述。具体来说:

  • 如果两边有英文双引号,表示 BINARY(32) 类型。例如 "abc"
  • 如果两边有英文双引号而且带有 L 前缀,表示 NCHAR(32) 类型。例如 L"报错信息"
  • 对空格、等号(=)、逗号(,)、双引号(”),前面需要使用反斜杠(\)进行转义(都指的是英文半角符号)。
  • 数值类型将通过后缀来区分数据类型,如后缀为无或 f64,映射的数据类型为 double;后缀为 f32,映射为 float,可移步 Schemaless 写入 | TDengine 文档 | 涛思数据 了解更多的后缀及映射类型。
  • t, T, true, True, TRUE, f, F, false, False 将直接作为 BOOL 型来处理。

例如如下数据行表示:向名为 st 的超级表下的 t1 标签为 “3”(NCHAR)、t2 标签为 “4”(NCHAR)、t3 标签为 “t3″(NCHAR)的数据子表,写入 c1 列为 3(BIGINT)、c2 列为 false(BOOL)、c3 列为 “passit”(BINARY)、c4 列为 4(DOUBLE)、主键时间戳为 1626006833639000000 的一行数据。

st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000

需要注意的是,如果描述数据类型后缀时使用了错误的大小写,或者为数据指定的数据类型有误,均可能引发报错提示而导致数据写入失败。

无模式写入的主要处理逻辑

无模式写入使用如下规则来生成子表名:首先将 measurement 的名称和标签的 key 和 value 组合成为如下的字符串——

"measurement,tag_key1=tag_value1,tag_key2=tag_value2"

需要注意的是,这里的 tagkey1,tag_key2 并不是用户输入的标签的原始顺序,而是使用了标签名称按照字符串升序排列后的结果,所以,tag_key1 并不是在行协议中输入的第一个标签。 排列完成以后计算该字符串的 MD5 散列值为 “md5_val”,我们将计算的结果与字符串组合生成表名:“t_md5_val”,其中的 “t” 是固定的前缀,每个通过该映射关系自动生成的表都具有该前缀。

用户可以通过在 taos.cfg 里配置 smlChildTableName 参数来指定生成的表名。 举例如下:配置 smlChildTableName=tname 插入数据为 st,tname=cpu1,t1=4 c1=3 1626006833639000000,则创建的表名为 cpu1,注意如果多行数据 tname 相同,但是后面的 tag_set 不同,则使用第一行自动建表时指定的 tag_set,其他的行会忽略。

此外,在处理行数据时,其他原则如下所示:

  1. 如果解析行协议获得的超级表不存在,则会自动创建这个超级表(不建议手动创建超级表,不然插入数据可能异常)。
  2. 如果解析行协议获得子表不存在,则 Schemaless 会按照步骤 1 或 2 确定的子表名来创建子表。
  3. 如果数据行中指定的标签列或普通列不存在,则在超级表中增加对应的标签列或普通列(只增不减)。
  4. 如果超级表中存在一些标签列或普通列未在一个数据行中被指定取值,那么这些列的值在这一行中会被置为 NULL。
  5. 对 BINARY 或 NCHAR 列,如果数据行中所提供值的长度超出了列类型的限制,自动增加该列允许存储的字符长度上限(只增不减),以保证数据的完整保存。
  6. 整个处理过程中遇到的错误会中断写入过程,并返回错误代码。
  7. 为了提高写入的效率,默认假设同一个超级表中 field_set 的顺序是一样的(第一条数据包含所有的 field,后面的数据按照这个顺序),如果顺序不一样,需要配置参数 smlDataFormat 为 false,否则,数据写入按照相同顺序写入,库中数据会异常。

注意,无模式所有的处理逻辑仍会遵循 TDengine 对数据结构的底层限制,例如每行数据的总长度不能超过 16KB。这方面的具体限制约束请参见 TDengine SQL 边界限制(命名与边界 | TDengine 文档 | 涛思数据)。

数据模式映射规则与变更处理

以 InfluxDB 行协议为例,其数据如何映射成为具有模式的数据?从上文中我们了解到,每个行协议中数据 measurement 映射为超级表名称,tag_set 中的标签名称为数据模式中的标签名,field_set 中的名称为列名称。以如下数据为例,说明映射规则:

st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4f64 1626006833639000000

该行数据映射生成一个超级表:st, 其包含了 3 个类型为 nchar 的标签,分别是:t1, t2, t3;五个数据列,分别是 ts(timestamp),c1 (bigint),c3(binary),c2 (bool), c4 (bigint)。映射成为如下 SQL 语句:

create stable st (_ts timestamp, c1 bigint, c2 bool, c3 binary(6), c4 bigint) tags(t1 nchar(1), t2 nchar(1), t3 nchar(2))

在数据模式变更处理中,不同行数据写入情况下,对于数据模式的影响也不同。在使用行协议写入一个明确标识的字段类型时,后续再更改该字段的类型定义,会出现明确的数据模式错误,即会触发写入 API 报告错误。如下所示:

st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4    1626006833639000000
st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4i   1626006833640000000

第一行的数据类型映射将 c4 列定义为 Double, 但是第二行的数据又通过数值后缀方式声明该列为 BigInt, 由此会触发无模式写入的解析错误。如果列前面的行协议将数据列声明为了 binary, 后续的要求长度更长的 binary 长度,此时会触发超级表模式的变更。

st,t1=3,t2=4,t3=t3 c1=3i64,c5="pass"     1626006833639000000
st,t1=3,t2=4,t3=t3 c1=3i64,c5="passit"   1626006833640000000

第一行中行协议解析会声明 c5 列是一个 binary(4)的字段,第二次行数据写入会提取列 c5 仍然是 binary 列,但是其宽度为 6,此时需要将 binary 的宽度增加到能够容纳新字符串的宽度。

st,t1=3,t2=4,t3=t3 c1=3i64               1626006833639000000
st,t1=3,t2=4,t3=t3 c1=3i64,c6="passit"   1626006833640000000

第二行数据相对于第一行来说增加了一个列 c6,类型为 binary(6)。那么此时会自动增加一个列 c6, 类型为 binary(6)。

写在最后

受篇幅所限,关于无模式写入的时间分辨率识别、写入完整性、错误码等内容请参考 Schemaless 写入 | TDengine 文档 | 涛思数据 。如有更多问题,欢迎加入 TDengine 用户交流群,届时将会有专业的技术人员为你解惑。

欢迎添加小T(VX:TDengine),加入物联网技术讨论群,第一时间了解TDengine 官方信息,与关注前沿技术的同学们共同探讨新技术、新玩法。


想了解更多 TDengine Database的具体细节,欢迎大家在GitHub上查看相关源代码。

TDengine:无模式写入行协议的四种方式相关推荐

  1. SPI协议概括、SPI总线工作的四种方式和SPI接口时序

    SPI协议概括: SPI总线是一种4线总线,因其硬件功能很强,所以与SPI有关的软件就相当简单,使中央处理器(Central Processing Unit,CPU)有更多的时间处理其他事务.正是因为 ...

  2. Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

    Linux进程通信的四种方式--共享内存.信号量.无名管道.消息队列|实验.代码.分析.总结 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须 ...

  3. VirtualBox虚拟机网络设置(四种方式)

    VirtualBox是什么 VirtualBox 是一款 x86 虚拟机软件.原由德国innotek公司开发,2008年Sun收购了Innotek,而Sun于2010年被Oracle收购,2010年1 ...

  4. android java 多线程,Android多线程的四种方式

    当我们启动一个App的时候,Android系统会启动一个Linux Process,该Process包含一个Thread,称为UI Thread或Main Thread.通常一个应用的所有组件都运行在 ...

  5. JDBC 连接数据库的四种方式

    JDBC 连接数据库的四种方式 每博一文案 师傅说,人这一辈子总有许多遗憾,许多无可奈何. 其实有些事情不用太过执着,放下牵绊你的生活无畏执念,你会收获一个更为广阔的天地. 想开,看开,放开这六个字, ...

  6. 访问数据库的四种方式

    IRIS 中支持的四种方式: SQL.Objects.REST 和 GraphQL   卡济米尔·马列维奇,<运动员>(1932) > > "你当然无法理解! 习惯了 ...

  7. 解决存储过程中数据安全问题的四种方式

    随着科技的不断发展,数据量也正在呈指数倍的增加.在这样一个大背景下,存储产品成为了时下市场中最热的产品.而数据的安全性也变得尤为重要.后端存储已经逐渐成为企业业务系统的核心和关键. 一般而言,我们有四 ...

  8. Android:按键响应方式第一种onClick属性,第二种方法接口类,第三种方式匿名内部类,第四种方式Activity

    一.在xml 中设置按键的onClick 绑定的函数 新建一个工程 然后在相关的页面添加一个Button 运行一下 实际上,现在这个按键一,我们去运行的时候,他是没有任何的反馈的,我们按完它,他没有任 ...

  9. Linux C++多线程同步的四种方式

    目录 一.互斥锁 二.条件变量 三.读写锁 原文链接:Linux C++多线程同步的四种方式(非常详细)_Y先森0.0-CSDN博客 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通 ...

最新文章

  1. Android面试题目之二:整形转换为字符串
  2. Asp.net中的两种刷新父窗体方法
  3. 【Android FFMPEG 开发】FFMPEG 解码 AVPacket 数据到 AVFrame ( AVPacket-解码器 | 初始化 AVFrame | 解码为 AVFrame 数据 )
  4. SAP 4.6C升级ECC6.0 WS_QUERY 的改法
  5. js get/set Cookie
  6. 新一代搜索引擎项目 ZeroSearch 设计探索
  7. Oracle数据文件scn不一致,数据文件SCN的一致性问题
  8. 浅谈:国内软件公司为何无法做大做强?
  9. PCI Express®Basics Background | PDF
  10. Android Screen
  11. unbuntu下 配置vim 详解
  12. 主流影视网站8合一H5视频源码自动更新数据
  13. 虚拟化服务器安装方法,Citrix Xenserver:7.0虚拟化服务器安装详细图文教程
  14. 【CV】ShuffleNet:通过 GroupConv 和 ChannelShuffle 实现轻量化 CNN 架构
  15. 解决多旅行商(MTSP)的分组遗传算法(GGA-SS)
  16. java block报错图_Poi读取Excle报错 java.util.zip.ZipException: invalid stored block lengths
  17. 支付宝账号转账/扫码即可到转账页
  18. 复杂大脑网络的结构和功能
  19. mysql常用的tamper脚本,tampermonkey 实用脚本
  20. JAVA中BitSet使用

热门文章

  1. 3大场景、4款新品公开亮相:「低速智能驾驶」新赛道惹关注
  2. 从失业到年薪百万,被你忽略的“一技之长”正在膨胀!
  3. Aqara首次发布会三大预测 全屋智能生活体验再次升级
  4. 微信内置浏览器私有接口WeixinJSBridge的常用方法
  5. 董明珠称格力宁可降工资也不裁员;阿里发布财年第四季度及全年财报
  6. 顶级域名.tk申请与DNSPOD解析
  7. 刚刚,Google AI 部门换帅,李飞飞宣布离职!
  8. 谈谈对儒家与道家的一些小小看法
  9. 原创2D/3D游戏引擎源代码开源
  10. 一杯苦咖啡 | 公司来了个漂亮女实习程序员