尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TDengine 基本概念详解:以智能电表为例掌握采集量、标签、超级表、子表与虚拟表

TDengine 基本概念详解:以智能电表为例掌握采集量、标签、超级表、子表与虚拟表 TDengine 基本概念详解以智能电表为例掌握采集量、标签、超级表、子表与虚拟表【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine时序数据库与传统关系型数据库最大的不同在于其围绕时间戳 设备 物理量组织数据。本文以智能电表为例系统讲解 TDengine TSDB下文简称 TDengine中最核心的基本概念——采集量、标签、数据采集点、表、超级表、子表、虚拟表、库与时间戳并配套给出可直接执行的建库、建表 SQL帮助你建立起一套清晰、可复用的时序数据建模思维为后续的数据写入与查询打下基础。本文内容以 docs/zh/04-quick-start/02-basic-concepts.md 为主线骨架并结合 docs/zh/04-quick-start/03-data-modeling.md 的建模示例与 docs/zh/05-tdengine-sql 相关章节进行扩展。完整的动手建模 SQL 可继续阅读 数据建模。示例数据以智能电表为统一模型为说明时序数据的基本概念并为后续示例提供统一的数据模型本文假设某型号智能电表能够采集电流、电压和相位这 3 个模拟量同时每块智能电表还具有位置和分组等静态属性。采集到的数据示例如下表所示。Device IDTimestampCurrentVoltagePhaseLocationGroup IDd1001153854868500010.32190.31California.SanFrancisco2d1002153854868400010.22200.23California.SanFrancisco3d1003153854868650011.52210.35California.LosAngeles3d1004153854868550013.42230.29California.LosAngeles2d1001153854869500012.62180.33California.SanFrancisco2d1004153854869660011.82210.28California.LosAngeles2d1002153854869665010.32180.25California.SanFrancisco3d1001153854869680012.32210.31California.SanFrancisco2上表展示了各设备 IDDevice ID对应的智能电表在特定时刻采集的物理量数据包括电流current、电压voltage和相位phase。除动态采集的数据外每块智能电表还带有一组静态标签tag例如位置location和分组 IDGroup ID。这些设备可以根据外部触发事件或预设周期持续采集数据从而形成不断更新的时序数据流。注意上表中的时间戳以长整数形式出现如1538548685000这是 TDengine 中时间戳的标准表示方式之一自 UNIX 纪元UTC 时间 1970 年 1 月 1 日 0 点起经过的毫秒数。关于时间戳与时区的更多细节见本文后面的时间戳与时区小节。基本概念概览如果你只是想先完成基础建模可以重点理解采集量、标签、数据采集点、表、超级表和子表虚拟表适用于更复杂的业务分析场景可以在后续需要时再深入了解。下文逐一展开。采集量采集量Metric是指通过各种传感器、设备或其他类型的采集点获取的物理量如电流、电压、温度、压力、GPS 等。由于这些物理量随时间不断变化采集数据的类型也可能不同包括整型、浮点型、布尔型和字符串等。随着时间积累存储的数据会持续增长。以智能电表为例current、voltage和phase是典型的采集量。在 TDengine 中一个采集量对应表中的一个列。TDengine 支持的列数据类型非常丰富参见 docs/zh/05-tdengine-sql/01-datatype.md常用的包括类型存储空间说明TIMESTAMP8 字节时间戳默认精度为毫秒可在创建数据库时设置为微秒或纳秒BOOL1 字节布尔类型TINYINT/TINYINT UNSIGNED1 字节有符号 / 无符号单字节整数SMALLINT/SMALLINT UNSIGNED2 字节有符号 / 无符号短整数INT/INT UNSIGNED4 字节有符号 / 无符号整数BIGINT/BIGINT UNSIGNED8 字节有符号 / 无符号长整数FLOAT4 字节单精度浮点数有效位数约 6–7 位DOUBLE8 字节双精度浮点数有效位数约 15–16 位BINARY/VARCHAR自定义单字节字符串建议只用于 ASCII 可见字符NCHAR自定义多字节字符串适合中文等多字节字符例如智能电表的电流current可定义为FLOAT数值变化平滑、精度要求适中电压voltage可定义为INT相位phase可定义为FLOAT而位置location则定义为VARCHAR(64)。标签标签Tag是指附着在传感器、设备或其他类型采集点上的静态属性这些属性通常不会随时间变化例如设备型号、颜色、设备所在地等。标签的数据类型可以是任意类型。尽管标签本身是静态的但在实际应用中用户可能需要修改、删除或添加标签。与采集量不同随着时间推移存储的标签数据量通常保持相对稳定不会呈现明显增长趋势。在智能电表示例中location和group_id是典型的标签。从存储与查询的角度看标签与采集量有本质区别采集量是随时间增长的动态数据写入频率高、数据量大标签是相对静态的元数据数据量稳定主要用于筛选与分类TDengine 会对标签建立索引超级表查询时可先通过标签条件过滤出目标子表集合再在子表上检索时序数据从而大幅减少扫描范围。数据采集点数据采集点Data Collection Point是指在一定的预设时间周期内或受到特定事件触发时负责采集物理量的硬件或软件设备。一个数据采集点可以同时采集一个或多个采集量但这些采集量都是在同一时刻获取的并拥有相同的时间戳。对于结构复杂的设备通常会有多个数据采集点每个数据采集点的采集周期可能各不相同它们之间完全独立互不干扰。以一辆汽车为例可能有专门的数据采集点用于采集 GPS有的数据采集点负责监控发动机状态还有的数据采集点则专注于车内环境监测。这样一辆汽车就包含了 3 个不同类型的数据采集点。在智能电表示例中d1001、d1002、d1003、d1004等标识符代表了不同的数据采集点。表一个数据采集点一张表鉴于采集的数据通常是结构化数据为了降低用户的学习难度TDengine 采用传统的关系型数据库模型来管理数据。同时为了充分发挥时序数据的特性TDengine 采取了一个数据采集点一张表的设计策略即要求为每个数据采集点单独建立一张表。例如若有千万块智能电表则在 TDengine 中需要创建相应数量的表。在智能电表示例数据中设备 ID 为d1001的智能电表对应 TDengine 中的一张表该电表采集的所有时序数据均存储于此表中。这种设计方式既保留了关系型数据库的易用性又充分利用了时序数据的独特优势。一个数据采集点一张表的设计有几大优点写入无需加锁由于不同数据采集点产生数据的过程完全独立每个数据采集点的数据源是唯一的一张表也就只有一个写入者这样就可采用无锁方式来写数据写入速度能大幅提升。追加式写入对于一个数据采集点而言其产生的数据按照时间递增因此写入操作可以通过追加方式实现进一步提高数据写入速度。连续块存储一个数据采集点的数据以块为单位连续存储。这样每次读取一个时间段的数据时可以大幅减少随机读取操作数量级提升读取和查询速度。列式存储与高压缩率一个数据块内部采用列式存储。对于不同的数据类型可以采用不同压缩算法来提高压缩率。同时由于采集量的变化通常是缓慢的压缩率会更高。如果采用传统方式将多个数据采集点的数据写入一张表由于网络延时不可控不同数据采集点的数据到达服务器的时序无法保证写入操作需要锁保护而且同一数据采集点的数据也难以保证连续存储在一起。采用一个数据采集点一张表的方式可以最大程度保证单个数据采集点的插入和查询性能并提高数据压缩率。在 TDengine 中通常使用数据采集点的名称如d1001作为表名。每个数据采集点可以有多个采集量如current、voltage、phase等每个采集量对应一张表的一列。采集量的数据类型可以是整型、浮点型、字符串等。此外表的第一列必须是时间戳即数据类型为timestamp。对于每个采集量TDengine 将使用第一列时间戳建立索引并采用列式存储。对于汽车等复杂设备由于它有多个数据采集点因此需要为一辆汽车建立多张表。从源码结构看一表一写入者 时间递增追加的设计直接决定了 TDengine 的存储与写入路径写入侧只需按时间顺序追加数据块无需维护复杂的锁与随机更新逻辑这也是列式块存储block-based storage能够高效落盘的前提。相关实现可进一步在 source/libs/wal 的 WAL 写入逻辑中印证。超级表TDengine 采用一个数据采集点一张表的设计虽然有利于高效地管理每个数据采集点但随着设备数量不断增加表的数量也会急剧增加这给表的管理以及表之间的聚合带来了巨大的挑战。为了解决这个问题TDengine 引入超级表Super Table简称 STable的概念。超级表是一种数据结构它能将某一特定类型的数据采集点聚集在一起形成一张逻辑上的统一表。这些数据采集点具有相同的表结构但各自的静态属性如标签可能不同。创建超级表时除了定义采集量的结构之外还需定义超级表的标签。一张超级表至少包含一个时间戳列、一个或多个采集量列以及一个或多个标签列。此外超级表的标签可以灵活地进行增加、修改或删除操作。在 TDengine 中表代表具体的数据采集点而超级表则代表一组具有相同属性的数据采集点集合。以智能电表为例可以为该类型的电表创建一张超级表其中包含所有智能电表的共有属性包括动态的时序数据以及静态的标签数据。这种设计不仅简化了表的管理还便于进行跨数据采集点的聚合操作从而提高数据处理效率。-- 创建超级表 meters包含采集量current/voltage/phase与标签location/group_id CREATE STABLE meters ( ts timestamp, current float, voltage int, phase float ) TAGS ( location varchar(64), group_id int );创建超级表的规则要点第 1 列必须为时间戳列如ts timestamp第 2 列开始是采集量列数据类型可以为整型、浮点型、字符串等TAGS关键字后的括号中定义标签标签名、数据类型等标签的数据类型可以为整型、浮点型、字符串等标签的名称不能与采集量列的名称相同。子表子表Child Table是数据采集点在逻辑上的一种抽象表示它是隶属于某张超级表的具体表。用户可以将超级表的定义作为模板并通过指定子表的标签值来创建子表。这样通过超级表生成的表便被称为子表。超级表与子表之间的关系主要体现在以下几个方面。一张超级表包含多张子表这些子表具有相同的表结构但标签值各异。子表的表结构不能直接修改但可以修改超级表的列和标签且修改对所有子表立即生效。超级表定义了一个模板自身并不存储任何数据或标签信息。通过超级表创建子表d1001的 SQL 如下CREATE TABLE d1001 USING meters ( location, group_id ) TAGS ( California.SanFrancisco, 2 );其中CREATE TABLE为关键字表示创建子表d1001是子表的名称USING表示使用超级表meters作为模板超级表名后的括号中是标签列名列表TAGS后的括号中指定子表的标签列值。California.SanFrancisco和2表示子表d1001的位置为California.SanFrancisco分组 ID 为2。当对超级表进行写入或查询操作时用户可以使用伪列tbname指定或输出对应操作的子表名。为了更直观地理解采集量、标签、超级表与子表之间的关系可以参考下图的数据模型示意图。图中展示了超级表meters定义的公共结构ts timestamp、current float、voltage int、phase float等采集量以及location、group_id标签以及各子表通过包含关系连接到超级表、继承表结构但拥有各自标签值与表名的层级关系每个子表右侧是随时间增长、按时间戳组织的时序数据行。自动建表为了简化操作并确保数据顺利写入即使子表尚不存在用户也可以使用带有USING关键字的自动建表SQL 进行数据写入。这种机制允许系统在遇到不存在的子表时先自动创建该子表再执行数据写入操作如果子表已经存在系统会直接写入数据。INSERT INTO d1002 USING meters TAGS ( California.SanFrancisco, 2 ) VALUES ( NOW, 10.2, 219, 0.32 );上面 SQL 中INSERT INTO d1002表示向子表d1002写入数据USING meters表示以超级表meters为模板TAGS (California.SanFrancisco, 2)指定子表标签值VALUES (NOW, 10.2, 219, 0.32)表示插入一行记录其中NOW为客户端当前时间戳。执行时若子表d1002不存在系统会先自动建表再写入。子表与普通表的区别在 TDengine 中除了具有标签的子表以外还存在一种不带任何标签的普通表它与普通关系型数据库中的表类似。二者区别如下标签扩展性子表在普通表的基础上增加了静态标签可携带更多元数据信息且标签可变可增加、删除或修改。表归属子表总是隶属于某张超级表普通表则独立存在不属于任何超级表。转换限制普通表无法直接转换为子表子表也无法转换为普通表表类型在创建时即确定。创建普通表的 SQL 如下CREATE TABLE meter_n ( ts timestamp, current float, voltage int, phase float, location varchar(64), group_id int );需要说明的是采用普通表作为数据模型意味着静态标签数据如location和group_id会重复存储在表的每一行中不仅增加存储空间消耗查询时也无法直接利用标签数据进行过滤查询性能会低于使用超级表的数据模型。因此在 IIoT 场景下应优先考虑超级表 子表的建模方式。库表的逻辑集合与存储策略容器库Database是 TDengine 中用于管理一组表的集合。TDengine 允许一个运行实例包含多个库并且每个库都可以配置不同的存储策略。由于不同类型的数据采集点通常具有不同的数据特征例如数据采集频率、数据保留期限、副本数量、数据块大小等为了在各种场景下确保 TDengine 能够发挥最大效率建议将具有不同数据特征的超级表创建在不同的库中。在一个库中可以包含一到多张超级表但每张超级表只能属于一个库。同时一张超级表所拥有的所有子表也都将存储在该库中。这种设计有助于实现更细粒度的数据管理和优化确保 TDengine 能够根据不同数据特征提供最佳的处理性能。创建一个用于存储电表数据的数据库的 SQL 如下CREATE DATABASE power PRECISION ms KEEP 3650 DURATION 10 BUFFER 16;各参数说明如下PRECISION ms该数据库的时序数据使用毫秒ms精度的时间戳可选值还有us微秒与ns纳秒默认值为msKEEP 3650数据保留 3650 天超过该时限的数据将被自动删除DURATION 10每 10 天的数据放在一个数据文件中即数据文件覆盖的时间跨度BUFFER 16写入使用大小为 16 MB 的内存池。创建power数据库后可以执行USE语句切换数据库之后的插入、查询等操作都在power数据库中进行USE power;这几个参数的具体语义与取值边界在 docs/zh/05-tdengine-sql/02-ddl/01-database.md 中有更详细的说明要点如下PRECISION数据库时间戳精度ms毫秒默认、us微秒、ns纳秒三选一一旦确定即影响该库所有时间戳的字面量单位。KEEP默认值为3650天取值范围为[1, 365000]且必须大于或等于DURATION参数值的 3 倍支持带单位写法如KEEP 100h、KEEP 10d不加单位时默认单位为天。企业版还支持多级存储可设置最多 3 个保存时间。DURATION数据文件存储数据的时间跨度默认值为10d取值范围为[60m, 3650d]支持m分钟、h小时、d天单位。BUFFER一个 vnode 的写入内存池大小单位为 MB默认值为256最小为3最大为16384。因此上文示例中的BUFFER 16属于偏小的取值接近下限在真实生产环境中通常应结合写入吞吐评估并适当调大。虚拟表先写入、后建模一个数据采集点一张表以及超级表的设计解决了工业和物联网等场景下的大多数时序数据管理和分析难题。但在真实场景中一个设备往往有多种传感器数据采集频次也可能相差很大。例如对于一台风机电气参数、环境参数、机械参数各自对应的传感器和采集频次可能完全不同。因此很难用一张真实表描述一台设备往往需要多张表。当需要综合多个传感器的数据进行分析计算时只能通过多级关联查询实现这可能带来易用性和性能问题。从用户角度看一个设备一张表更直观也更容易操作。但如果在建模之初直接采用一个设备一张表的设计由于采集频次不同每一个具体时间戳下都可能存在大量空值列从而降低存储和查询效率。为了解决这个问题TDengine 引入虚拟表Virtual Table简称 VTable的概念。虚拟表不存储实际数据但可以用于分析计算。它的数据来源于真实存储数据的子表或普通表并通过按时间戳排序、对齐和合并各原始表中的不同列生成。与真实表类似虚拟表也可以分为虚拟超级表、虚拟子表和虚拟普通表。虚拟超级表可以表示一个设备或一组分析计算所需数据的完整集合每个虚拟子表可以根据需要引用相同或不同的列因此可以按业务视角灵活定义。虚拟表不能写入或删除数据查询方式与真实表相同。TDengine 支持在虚拟超级表、虚拟子表和虚拟普通表上执行查询。区别在于虚拟表的数据在每次查询时动态生成只有查询中引用的列才会被合并进虚拟表因此同一个虚拟表在不同查询中呈现和扫描的数据可能完全不同。先写入后建模虚拟表机制让先写入后建模成为现实。在数据采集和写入阶段无需预先为复杂业务分析设计表结构可以按照最接近设备协议的方式如单列模型直接写入 TDengine。待数据存储后再根据实际分析需求通过虚拟表动态创建适合业务视角的数据模型。这可以降低数据接入复杂度和初期建模负担使数据处理流程更加灵活高效。虚拟超级表的主要功能特点列选择与拼接用户可以从多个原始表中选择指定的列按需组合到一张虚拟表中形成统一的数据视图。基于时间戳对齐以时间戳为依据对数据进行对齐如果多个表在相同时间戳下存在数据则对应列的值组合成同一行若部分表在该时间戳下无数据则对应列填充为NULL。动态更新虚拟表根据原始表的数据变化自动更新确保数据的实时性。虚拟表不需要实际存储计算在生成时动态完成。通过引入虚拟表TDengine 可以更方便地管理更大、更复杂的设备数据。无论每个采集点采用单列模型还是多列模型也无论这些采集点的数据分布在一个库还是多个库中都可以通过定义虚拟表跨库、跨表指定数据源并通过虚拟超级表进行跨数据采集点的聚合运算从而实现一个设备一张表的分析视角。从语法实现看虚拟表是 TDengine SQL 语法层面的一等公民在 source/libs/parser/inc/sql.y 中table_options支持VIRTUAL关键字VIRTUAL NK_INTEGER对应建表语句末尾的VIRTUAL 1选项同时table_kind也能识别VIRTUAL以支持SHOW虚拟表。创建虚拟表的关键字是CREATE VTABLE虚拟表与CREATE STABLE ... VIRTUAL 1虚拟超级表完整示例可参考 docs/zh/04-quick-start/03-data-modeling.md。虚拟表典型场景单源多维度时序聚合在单源多维度时序聚合场景中单源指来自同一数据采集点下的多个单列时序数据表。这些数据因业务需求或其他限制被拆分为多个单列存储的表但通过设备标签和时间基准保持逻辑一致性。虚拟表在此场景中的作用是将一个采集点中纵向拆分的数据还原为完整的横向状态。例如建模时对电流、电压和相位分别建 3 张超级表单列模型之后通过虚拟超级表将这 3 种采集量聚合到一张表中统一查询-- 创建单列模型的超级表以电流为例 CREATE STABLE current_stb ( ts timestamp, current float ) TAGS ( device_id varchar(64), location varchar(64), group_id int ); -- 创建虚拟超级表将电流、电压、相位聚合为一张表 CREATE STABLE meters_v ( ts timestamp, current float, voltage int, phase float ) TAGS ( location varchar(64), group_id int ) VIRTUAL 1; -- 为设备 d1001 创建虚拟子表指定各列的数据来源 CREATE VTABLE d1001_v ( current FROM current_d1001.current, voltage FROM voltage_d1001.voltage, phase FROM phase_d1001.phase ) USING meters_v TAGS ( California.SanFrancisco, 2 );当原始表current_d1001、voltage_d1001、phase_d1001的时间戳不完全对齐时虚拟表会按时间戳对齐并填充NULL。例如对虚拟表执行SELECT * FROM d1001_v;得到的结果形如tscurrentvoltagephase153854868500010.32190.31153854869500012.62180.33153854869680012.32210.31153854869710012.1220NULL1538548697200NULLNULL0.32153854869770011.8NULLNULL1538548697800NULL2220.33可以看到某一时间戳下缺少数据的列会被自动填充为NULL这正是虚拟表按时间戳对齐合并机制的直观体现。虚拟表典型场景跨源采集量对比分析在跨源采集量对比分析中跨源指数据来自不同数据采集点。从不同数据采集点中提取具有可比语义的采集量后可以通过虚拟表将这些采集量按照时间戳进行对齐和合并并进行对比分析。例如将来自不同设备的电流数据聚合到一张虚拟表中进行对比CREATE VTABLE current_v ( ts timestamp, d1001_current float FROM current_d1001.current, d1002_current float FROM current_d1002.current, d1003_current float FROM current_d1003.current, d1004_current float FROM current_d1004.current );查询SELECT * FROM current_v;的结果按时间戳对齐同一行的各列分别对应 4 个设备在同一时刻的电流值若某设备在该时间戳无数据则对应列填充NULL。这样跨设备的对比分析如波动一致性、异常识别就可以像查询普通表一样完成。时间戳与时区时间戳在时序数据处理中扮演着至关重要的角色特别是在应用程序需要从多个不同时区访问数据库时这一问题变得更加复杂。在深入了解 TDengine 如何处理时间戳与时区之前先介绍以下几个基本概念。本地日期时间指特定地区的当地时间通常表示为yyyy-MM-dd hh:mm:ss.SSS格式的字符串不包含任何时区信息如2021-07-21 12:00:00.000。时区指地球上不同地理位置的标准时间。协调世界时Universal Time CoordinatedUTC或格林尼治时间是国际时间标准其他时区通常表示为相对于 UTC 的偏移量如UTC8代表东八区时间。UTC 时间戳表示自 UNIX 纪元即 UTC 时间 1970 年 1 月 1 日 0 点起经过的毫秒数。例如1700000000000对应的日期时间是2023-11-14 22:13:20UTC0。在 TDengine 中保存时序数据时实际保存的是UTC 时间戳。TDengine 在写入数据时时间戳的处理分为以下两种情况RFC-3339 格式当使用这种格式时TDengine 能够正确解析带有时区信息的时间字符串为 UTC 时间戳。例如2018-10-03T14:38:05.00008:00会被转换为 UTC 时间戳。非 RFC-3339 格式如果时间字符串不包含时区信息TDengine 将使用应用程序所在的时区设置自动将时间转换为 UTC 时间戳。在查询数据时TDengine 客户端会根据应用程序当前的时区设置自动将保存的 UTC 时间戳转换成本地时间进行显示确保用户在不同时区下都能看到正确的时间信息。补充说明TDengine 的时间戳字面量还支持NOW客户端当前时间以及时间表达式运算如NOW - 2h长整数时间戳的单位由数据库的PRECISION决定——毫秒精度表示毫秒数微秒精度表示微秒数纳秒精度表示纳秒数。更完整的类型与精度说明参见 docs/zh/05-tdengine-sql/01-datatype.md。多列模型与单列模型TDengine 支持灵活的数据模型设计包括多列模型和单列模型。多列模型允许将多个由同一数据采集点同时采集且时间戳一致的物理量作为不同列存储在同一张超级表中写入效率和存储效率通常更优也是官方推荐的建模方式。然而在某些极端情况下可能会采用单列模型即每个采集的物理量都单独建立一张表。例如对于电流、电压和相位这 3 种物理量分别建立 3 张超级表。单列模型的适用场景当一个数据采集点的采集量种类经常发生变化时如果采用多列模型就需要频繁修改超级表的结构定义增加应用程序的复杂性此时采用单列模型可以独立地管理和扩展每个物理量的超级表简化应用设计与维护。无论选择哪种模型都可以借助虚拟表进行跨表运算弥补单列模型在分析易用性上的不足——这正是上文虚拟表机制的用武之地。总结与继续阅读本文以智能电表为例梳理了 TDengine 数据建模的完整概念体系动态与静态分离随时间增长的采集量列与相对静态的标签元数据分开管理一表一采集点以一个数据采集点一张表保证写入无需加锁、数据连续存储配合列式存储与块存储提升读写与压缩性能超级表 子表以超级表作为同类设备的统一模板解决海量表管理困难与跨表聚合问题虚拟表以先写入后建模的方式按业务视角动态聚合多源数据实现跨表、跨库的灵活分析库与时间戳通过不同存储策略的库承载不同数据特征的超级表并以 UTC 时间戳统一处理跨时区场景。理解这些概念后可以继续阅读 数据建模使用 SQL 创建数据库、超级表、子表和虚拟表也可以进一步阅读 写入数据 与 查询与聚合 快速上手完整的数据写入与查询流程。【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表