@ -6,14 +6,14 @@
| Device ID | Time Stamp | current | voltage | phase | location | groupId |
| :-------: | :-----------: | :-----: | :-----: | :---: | :--------------: | :-----: |
| D 1001 | 1538548685000 | 10.3 | 219 | 0.31 | Beijing.Chaoyang | 2 |
| D 1002 | 1538548684000 | 10.2 | 220 | 0.23 | Beijing.Chaoyang | 3 |
| D 1003 | 1538548686500 | 11.5 | 221 | 0.35 | Beijing.Haidian | 3 |
| D 1004 | 1538548685500 | 13.4 | 223 | 0.29 | Beijing.Haidian | 2 |
| D 1001 | 1538548695000 | 12.6 | 218 | 0.33 | Beijing.Chaoyang | 2 |
| D 1004 | 1538548696600 | 11.8 | 221 | 0.28 | Beijing.Haidian | 2 |
| D 1002 | 1538548696650 | 10.3 | 218 | 0.25 | Beijing.Chaoyang | 3 |
| D 1001 | 1538548696800 | 12.3 | 221 | 0.31 | Beijing.Chaoyang | 2 |
| d 1001 | 1538548685000 | 10.3 | 219 | 0.31 | Beijing.Chaoyang | 2 |
| d 1002 | 1538548684000 | 10.2 | 220 | 0.23 | Beijing.Chaoyang | 3 |
| d 1003 | 1538548686500 | 11.5 | 221 | 0.35 | Beijing.Haidian | 3 |
| d 1004 | 1538548685500 | 13.4 | 223 | 0.29 | Beijing.Haidian | 2 |
| d 1001 | 1538548695000 | 12.6 | 218 | 0.33 | Beijing.Chaoyang | 2 |
| d 1004 | 1538548696600 | 11.8 | 221 | 0.28 | Beijing.Haidian | 2 |
| d 1002 | 1538548696650 | 10.3 | 218 | 0.25 | Beijing.Chaoyang | 3 |
| d 1001 | 1538548696800 | 12.3 | 221 | 0.31 | Beijing.Chaoyang | 2 |
< center > 表1: 智能电表数据示例< / center >
@ -39,13 +39,13 @@
因为采集的数据一般是结构化数据, 而且为降低学习门槛, TDengine采用传统的关系型数据库模型管理数据。因此用户需要先创建库, 然后创建表, 之后才能插入或查询数据。TDengine采用的是结构化存储, 而不是NoSQL的key-value存储。
### 一个数据采集点一张表
为充分利用其数据的时序性和其他数据特点, TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的D1001, D1002, D1003, D 1004都需单独建表) , 用来存储这个采集点所采集的时序数据。这种设计有几大优点:
为充分利用其数据的时序性和其他数据特点, TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的d1001, d1002, d1003, d 1004都需单独建表) , 用来存储这个采集点所采集的时序数据。这种设计有几大优点:
1. 能保证一个采集点的数据在存储介质上是一块一块连续的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。
2. 由于不同采集设备产生数据的过程完全独立,每个设备的数据源是唯一的,一张表也就只有一个写入者,这样就可采用无锁方式来写,写入速度就能大幅提升。
3. 对于一个数据采集点而言,其产生的数据是时序的,因此写的操作可用追加的方式实现,进一步大幅提高数据写入速度。
如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。**
如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个数据 采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。**
TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。每个数据采集点可能同时采集多个物理量(如上表中的curent, voltage, phase),每个物理量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集的数据, TDengine将自动按照时间戳建立索引, 但对采集的物理量不建任何索引。数据是用列式存储方式保存。
@ -58,10 +58,10 @@ TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。
当对多个具有相同数据类型的数据采集点进行聚合操作时, TDengine将先把满足标签过滤条件的表从超级表的中查找出来, 然后再扫描这些表的时序数据, 进行聚合操作, 这样能将需要扫描的数据集大幅减少, 从而大幅提高聚合计算的性能。
##集群与基本逻辑单元
## 集群与基本逻辑单元
TDengine 的设计是基于单个硬件、软件系统不可靠,基于任何单台计算机都无法提供足够计算能力和存储能力处理海量数据的假设进行设计的。因此 TDengine 从研发的第一天起, 就按照分布式高可靠架构进行设计, 是支持水平扩展的, 这样任何单台或多台服务器发生硬件故障或软件错误都不影响系统的可用性和可靠性。同时, 通过节点虚拟化并辅以自动化负载均衡技术, TDengine 能最高效率地利用异构集群中的计算和存储资源降低硬件投资。
###主要逻辑单元
### 主要逻辑单元
TDengine 分布式架构的逻辑结构图如下:
< center > < img src = "../assets/structure.png" > < / center >
< center > 图 1 TDengine架构示意图 < / center >
@ -71,11 +71,11 @@ TDengine 分布式架构的逻辑结构图如下:
**数据节点(dnode):** dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例, 一个工作的系统必须有至少一个数据节点。dnode包含零到多个逻辑的虚拟节点(VNODE),零或者至多一个逻辑的管理节点(mnode). dnode在系统中的唯一标识由实例的End Point(EP)决定。EP是dnode所在物理节点的FQDN(Fully Qualified Domain Name)和系统所配置的网络端口号(Port)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。
**虚拟节点(vnode)**: 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode, 图中V)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个DB, 但一个DB可以有多个 vnode。一个 vnode 除存储的时序数据外, 也保存有所包含的表的SCHEMA、标签值等。一个虚拟节点由所属的数据节点的EP, 以及所属的Vgroup ID在系统内唯一标识, 是由管理节点创建并管理的。
**虚拟节点(vnode)**: 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode, 图中V2, V3, V4等 )。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个DB, 但一个DB可以有多个 vnode。一个 vnode 除存储的时序数据外, 也保存有所包含的表的SCHEMA、标签值等。一个虚拟节点由所属的数据节点的EP, 以及所属的Vgroup ID在系统内唯一标识, 是由管理节点创建并管理的。
**管理节点(mnode):** 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中M)。同时,管理节点也负责元数据(包括用户、数据库、表、静态标签等)的存储和管理,因此也称为 Meta Node。TDengine 集群中可配置多个(最多不超过5个) mnode, 它们自动构建成为一个管理节点集群 (图中M0, M1, M2)。mnode 间采用 master/slave 的机制进行管理,而且采取强一致方式进行数据同步, 任何数据更新操作只能在 Master 上进行。mnode 集群的创建由系统自动完成, 无需人工干预。每个dnode上至多有一个mnode, 由所属的数据节点的EP来唯一标识。每个dnode通过内部消息交互自动获取整个集群中所有 mnode 所在的 dnode 的EP。
**管理节点(mnode):** 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中M)。同时,管理节点也负责元数据(包括用户、数据库、表、静态标签等)的存储和管理,因此也称为 Meta Node。TDengine 集群中可配置多个(最多不超过5个) mnode, 它们自动构建成为一个虚拟管理节点组 (图中M0, M1, M2)。mnode 间采用 master/slave 的机制进行管理,而且采取强一致方式进行数据同步, 任何数据更新操作只能在 Master 上进行。mnode 集群的创建由系统自动完成, 无需人工干预。每个dnode上至多有一个mnode, 由所属的数据节点的EP来唯一标识。每个dnode通过内部消息交互自动获取整个集群中所有 mnode 所在的 dnode 的EP。
**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vnode group)来保证系统的高可靠。虚拟节点组内采取master/slave的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode, 这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个DB的副本数为N, 系统必须有至少N个数据节点。副本数在创建DB时通过参数 replica 可以指定, 缺省为1。使用 TDengine 的多副本特性, 可以不再需要昂贵的磁盘阵列等存储设备, 获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理, 并且由管理节点分配一系统唯一的ID, vnode group ID。如果两个虚拟节点的vnode group ID相同, 说明他们属于同一个组, 数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的, 容许只有一个, 也就是没有数据复制。Vnode group ID是永远不变的, 即使一个虚拟节点组被删除, 它的ID也不会被收回重复利用。
**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vnode group)来保证系统的高可靠。虚拟节点组内采取master/slave的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode, 这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个DB的副本数为N, 系统必须有至少N个数据节点。副本数在创建DB时通过参数 replica 可以指定, 缺省为1。使用 TDengine 的多副本特性, 可以不再需要昂贵的磁盘阵列等存储设备, 获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理, 并且由管理节点分配一系统唯一的ID, vnode group ID。如果两个虚拟节点的vnode group ID相同, 说明他们属于同一个组, 数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的, 容许只有一个, 也就是没有数据复制。Vnode group ID是永远不变的, 即使一个虚拟节点组被删除, 它的ID也不会被收回重复利用。
**TAOSC:** taosc是TDengine给应用提供的驱动程序(driver), 负责处理应用与集群的接口交互, 内嵌于JDBC、ODBC driver中, 或者C、Python、Go语言连接库里。应用都是通过taosc, 而不是直接连接集群中的数据节点与整个集群进行交互的。这个模块负责获取并缓存元数据; 将插入、查询等请求转发到正确的数据节点; 在把结果返回给应用时, 还需要负责最后一级的聚合、排序、过滤等操作。对于JDBC, ODBC, C/C++接口而言, 这个模块是在应用所处的物理节点上运行, 但消耗的资源很小。同时, 为支持全分布式的RESTful接口, taosc在TDengine集群的每个dnode上都有一运行实例。
@ -96,7 +96,7 @@ TDengine 分布式架构的逻辑结构图如下:
**重定向**: 无论是dnode还是taosc, 最先都是要发起与mnode的链接, 但mnode是系统自动创建并维护的, 因此对于用户来说, 并不知道哪个dnode在运行mnode。TDengine只要求向系统中任何一个工作的dnode发起链接即可。因为任何一个正在运行的dnode, 都维护有目前运行的mnode EP List。当收到一个来自新启动的dnode或taosc的链接请求, 如果自己不是mnode, 则将mnode EP List回复给对方, taosc或新启动的dnode收到这个list, 就重新尝试建立链接。当mnode EP List发生改变, 通过节点之间的消息交互, 各个数据节点就很快获取最新列表, 并通知taosc。
###一典型的操作流程
### 一典型的操作流程
为解释vnode, mnode, taosc和应用之间的关系以及各自扮演的角色, 下面对写入数据这个典型操作的流程进行剖析。
< center > < img src = "../assets/message.png" > < / center >
< center > 图 2 TDengine典型的操作流程 < / center >
@ -117,7 +117,7 @@ TDengine 分布式架构的逻辑结构图如下:
## 存储模型与数据分区、分片
###存储模型
### 存储模型
TDengine存储的数据包括采集的时序数据以及库、表相关的元数据、标签数据等, 这些数据具体分为三部分:
- 时序数据: 存放于vnode里, 由data、head和last三个文件组成, 数据量大, 查询量取决于应用场景。容许乱序写入, 但暂时不支持删除和更新操作。通过采用一个采集点一张表的模型, 一个时间段的数据是连续存储, 对单张表的写入是简单的追加操作, 一次读, 可以读到多条记录, 这样保证对单个采集点的插入和查询操作, 性能达到最优。
@ -129,7 +129,7 @@ TDengine存储的数据包括采集的时序数据以及库、表相关的元数
- 能够极大地降低标签数据存储的冗余度: 一般的NoSQL数据库或时序数据库, 采用的K-V存储, 其中的Key包含时间戳、设备ID、各种标签。每条记录都带有这些重复的内容, 浪费存储空间。而且如果应用要在历史数据上增加、修改或删除标签, 需要遍历数据, 重写一遍, 操作成本极其昂贵。
- 能够实现极为高效的多表之间的聚合查询:做多表之间聚合查询时,先把符合标签过滤条件的表查找出来,然后再查找这些表相应的数据块,这样大幅减少要扫描的数据集,从而大幅提高查询效率。而且标签数据采用全内存的结构进行管理和维护,千万级别规模的标签数据查询可以在毫秒级别返回。
###数据分片
### 数据分片
对于海量的数据管理,为实现水平扩展,一般都需要采取分片(Sharding)分区(Partitioning)策略。TDengine是通过vnode来实现数据分片的, 通过一个时间段一个数据文件来实现时序数据分区的。
vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和计算功能。为便于负载均衡、数据恢复、支持异构环境, TDengine将一个数据节点根据其计算和存储资源切分为多个vnode。这些vnode的管理是TDengine自动完成的, 对应用完全透明。
@ -140,12 +140,12 @@ vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和
每张表的meda data( 包含schema, 标签等) 也存放于vnode里, 而不是集中存放于mnode, 实际上这是对Meta数据的分片, 这样便于高效并行的进行标签过滤操作。
###数据分区
TDengine除vnode分片之外, 还按照时间段进行分区。每个数据文件只包含一个时间段的时序数据, 时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略, 只要数据文件超过规定的天数( 系统配置参数keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。
### 数据分区
TDengine除vnode分片之外, 还对时序数据 按照时间段进行分区。每个数据文件只包含一个时间段的时序数据, 时间段的长度由DB的配置参数days决定。这种按时间段分区的方法还便于高效实现数据的保留策略, 只要数据文件超过规定的天数( 系统配置参数keep),将被自动删除。而且不同的时间段可以存放于不同的路径和存储介质,以便于大数据的冷热管理,实现多级存储。
总的来说,**TDengine是通过vnode以及时间两个维度, 对大数据进行切分**,便于并行高效的管理,实现水平扩展。
###负载均衡
### 负载均衡
每个dnode都定时向 mnode(虚拟管理节点)报告其状态( 包括硬盘空间、内存大小、CPU、网络、虚拟节点个数等) , 因此mnode了解整个集群的状态。基于整体状态, 当mnode发现某个dnode负载过重, 它会将dnode上的一个或多个vnode挪到其他dnode。在挪动过程中, 对外服务继续进行, 数据插入、查询和计算操作都不受影响。
如果mnode一段时间没有收到dnode的状态报告, mnode会认为这个dnode已经离线。如果离线时间超过一定时长( 时长由配置参数offlineThreshold决定) , 该dnode将被mnode强制剔除出集群。该dnode上的vnodes如果副本数大于一, 系统将自动在其他dnode上创建新的副本, 以保证数据的副本数。如果该dnode上还有mnode, 而且mnode的副本数大于一, 系统也将自动在其他dnode上创建新的mnode, 以保证mnode的副本数。
@ -154,11 +154,12 @@ TDengine除vnode分片之外, 还按照时间段进行分区。每个数据文
负载均衡过程无需任何人工干预,应用也无需重启,将自动连接新的节点,完全透明。
##数据写入与复制流程
## 数据写入与复制流程
如果一个数据库有N个副本, 那一个虚拟节点组就有N个虚拟节点, 但是只有一个是Master, 其他都是slave。当应用将新的记录写入系统时, 只有Master vnode能接受写的请求。如果slave vnode收到写的请求, 系统将通知taosc需要重新定向。
###Master vnode写入流程
### Master vnode写入流程
Master Vnode遵循下面的写入流程:
< center > < img src = "../assets/write_master.png" > < / center >
< center > 图 3 TDengine Master写入流程 < / center >
1. Master vnode收到应用的数据插入请求, 验证OK, 进入下一步;
2. 如果系统配置参数walLevel打开( 设置为2) , vnode将把该请求的原始数据包写入数据库日志文件WAL, 以保证TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据, 避免数据的丢失;
@ -170,6 +171,7 @@ Master Vnode遵循下面的写入流程:
### Slave vnode写入流程
对于slave vnode, 写入流程是:
< center > < img src = "../assets/write_slave.png" > < / center >
< center > 图 4 TDengine Slave写入流程 < / center >
1. Slave vnode收到Master vnode转发了的数据插入请求。
2. 如果系统配置参数walLevl设置为2, vnode将把该请求的原始数据包写入日志(WAL);
@ -202,24 +204,24 @@ Vnode会保持一个数据版本号(Version),对内存数据进行持久化存
3. 在线的虚拟节点数过半, 而且有虚拟节点是slave的话, 该虚拟节点自动成为master
4. 对于2和3, 如果多个虚拟节点满足成为master的要求, 那么虚拟节点组的节点列表里, 最前面的选为master
更多的关于数据复制的流程,请见[TDengine 2.0 数据复制模块设计](https://jira.taosdata.com:18090/pages/viewpage.action?pageId=6266055) 。
更多的关于数据复制的流程,请见< a href = "https://www.taosdata.com/cn/documentation20/replica/" > TDengine 2.0数据复制模块设计< / a > 。
###同步复制
### 同步复制
对于数据一致性要求更高的场景, 异步数据复制无法满足要求, 因为有极小的概率丢失数据, 因此TDengine提供同步复制的机制供用户选择。在创建数据库时, 除指定副本数replica之外, 用户还需要指定新的参数quorum。如果quorum大于一, 它表示每次Master转发给副本时, 需要等待quorum-1个回复确认, 才能通知应用, 数据在slave已经写入成功。如果在一定的时间内, 得不到quorum-1个回复确认, master vnode将返回错误给应用。
采用同步复制, 系统的性能会有所下降, 而且latency会增加。因为元数据要强一致, Mnode之间的数据同步 就是采用的同步复制。
采用同步复制, 系统的性能会有所下降, 而且latency会增加。因为元数据要强一致, mnode之间的数据同步缺省 就是采用的同步复制。
注: vnode之间的同步复制仅仅企业版支持
##缓存与持久化
###缓存
## 缓存与持久化
### 缓存
TDengine采用时间驱动缓存管理策略( First-In-First-Out, FIFO) , 又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式( Least-Recent-Used, LRU) , 直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候, 将最早的数据批量写入磁盘。一般意义上来说, 对于物联网数据的使用, 用户最为关心的是刚产生的数据, 即当前状态。TDengine充分利用这一特性, 将最近到达的( 当前状态) 数据保存在缓存中。
TDengine通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中, 可以更加快速地响应用户针对最近一条或一批数据的查询分析, 整体上提供更快的数据库查询响应能力。从这个意义上来说, **可通过设置合适的配置参数将TDengine作为数据缓存来使用, 而不需要再部署Redis或其他额外的缓存系统**, 可有效地简化系统架构, 降低运维的成本。需要注意的是, TDengine重启以后系统的缓存将被清空, 之前缓存的数据均会被批量写入磁盘, 缓存的数据将不会像专门的Key-value缓存系统再将之前缓存的数据重新加载到缓存中。
每个vnode有自己独立的内存, 而且由多个固定大小的内存块组成, 不同vnode之间完全隔离。数据写入时, 类似于日志的写法, 数据被顺序追加写入内存, 但每个vnode维护有自己的skip list, 便于迅速查找。当一半以上的内存块写满时, 启动落盘操作, 而且后续写的操作在新的内存块进行。这样, 一个vnode里有一半内存块是保留有最近的数据的, 以达到缓存、快速查找的目的。一个vnode的内存块的个数由配置参数blocks决定, 内存块的大小由配置参数cache决定。
###持久化存储
### 持久化存储
TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久化存储。当vnode中缓存的数据达到一定规模时, 为了不阻塞后续数据的写入, TDengine也会拉起落盘线程将缓存的数据写入持久化存储。TDengine在数据落盘时会打开新的数据库日志文件, 在落盘成功后则会删除老的数据库日志文件, 避免日志文件无限制的增长。
为充分利用时序数据特点, TDengine将一个vnode保存在持久化存储的数据切分成多个文件, 每个文件只保存固定天数的数据, 这个天数由系统配置参数days决定。切分成多个文件后, 给定查询的起止日期, 无需任何索引, 就可以立即定位需要打开哪些数据文件, 大大加快读取速度。
@ -234,7 +236,7 @@ TDengine采用数据驱动的方式让缓存中的数据写入硬盘进行持久
数据写入磁盘时, 根据系统配置参数comp决定是否压缩数据。TDengine提供了三种压缩选项: 无压缩、一阶段压缩和两阶段压缩, 分别对应comp值为0、1和2的情况。一阶段压缩根据数据的类型进行了相应的压缩, 压缩算法包括delta-delta编码、simple 8B方法、zig-zag编码、LZ4等算法。二阶段压缩在一阶段压缩的基础上又用通用压缩算法进行了压缩, 压缩率更高。
###多级存储
### 多级存储
在默认配置下, TDengine会将所有数据保存在/var/lib/taos目录下, 而且每个vnode的数据文件保存在该目录下的不同目录。为扩大存储空间, 尽量减少文件读取的瓶颈, 提高数据吞吐率 TDengine可通过配置系统参数dataDir让多个挂载的硬盘被系统同时使用。除此之外, TDengine也提供了数据分级存储的功能, 即根据数据文件的新老程度存储在不同的存储介质上。比如最新的数据存储在SSD上, 超过一周的数据存储在本地硬盘上, 超过4周的数据存储在网络存储设备上, 这样来降低存储成本, 而又保证高效的访问数据。数据在不同存储介质上的移动是由系统自动完成的, 对应用是完全透明的。数据的分级存储也是通过系统参数dataDir来配置。
dataDir的配置格式如下:
@ -258,36 +260,40 @@ dataDir /mnt/disk6/taos 2
注:多级存储功能仅企业版支持
##数据查询
## 数据查询
TDengine提供了多种多样针对表和超级表的查询处理功能, 除了常规的聚合查询之外, 还提供针对时序数据的窗口查询、统计聚合等功能。TDengine的查询处理需要客户端、vnode, mnode节点协同完成。
5.1 单表查询
### 单表查询
SQL语句的解析和校验工作在客户端完成。解析SQL语句并生成抽象语法树(Abstract Syntax Tree, AST),然后对其进行校验和检查。以及向管理节点(mnode)请求查询中指定表的元数据信息(table metadata)。
根据元数据信息中的FQDN 信息, 将查询请求序列化后发送到该表所在的数据节点( dnode) 。dnode接收到查询请求后, 识别出该查询请求指向的虚拟节点( vnode) , 将消息转发到vnode的查询执行队列。vnode的查询执行线程建立基础的查询执行环境, 并立即返回该查询请求, 同时开始执行该查询。
根据元数据信息中的End Point 信息, 将查询请求序列化后发送到该表所在的数据节点( dnode) 。dnode接收到查询请求后, 识别出该查询请求指向的虚拟节点( vnode) , 将消息转发到vnode的查询执行队列。vnode的查询执行线程建立基础的查询执行环境, 并立即返回该查询请求, 同时开始执行该查询。
客户端在获取查询结果的时候, dnode的查询执行队列中的工作线程会等待vnode执行线程执行完成, 才能将查询结果返回到请求的客户端。
5.2 时间轴聚合 、插值
### 按时间轴聚合、降采样 、插值
时序数据有别于普通数据的显著特征是每条记录均具有时间戳,因此针对具有时间戳数据在时间轴上进行聚合是不同于普通数据库的重要功能。从这点上来看,与流计算引擎的窗口查询有相似的地方。
在TDengine中引入关键词interval来进行时间轴上固定长度时间窗口的切分, 并按照时间窗口对数据进行聚合, 对窗口范围内的数据按需进行聚合。例如:
select count(*) from D1001 interval(1h)
```mysql
select count(*) from d1001 interval(1h)
```
针对D1001设备采集的数据, 按照1小时的时间窗口返回每小时存储的记录数量。
针对d 1001设备采集的数据, 按照1小时的时间窗口返回每小时存储的记录数量。
在需要连续获得查询结果的应用场景下, 如果给定的时间区间存在数据缺失, 会导致该区间数据结果也丢失。TDengine提供策略针对时间轴聚合计算的结果进行插值, 通过使用关键词Fill就能够对时间轴聚合结果进行插值。例如:
select count(*) from D1001 interval(1h) fill(prev)
```mysql
select count(*) from d1001 interval(1h) fill(prev)
```
针对D1001设备采集数据统计每小时记录数, 如果某一个小时不存在数据, 这返回之前一个小时的统计数据。TDengine提供前向插值(prev)、线性插值(linear)、NULL值填充(NULL)、特定值填充(value)。
针对d 1001设备采集数据统计每小时记录数, 如果某一个小时不存在数据, 这返回之前一个小时的统计数据。TDengine提供前向插值(prev)、线性插值(linear)、NULL值填充(NULL)、特定值填充(value)。
5.3 多表聚合查询
### 多表聚合查询
多表聚合查询与单表查询的整体流程相同,但是存在如下的差异:
1) 由于多表可能分布在不同的节点(dnode), 因此多表的聚合查询需要首先获得表所在的全部数据节点的信息, 并且同时向相关的dnode发出查询请求。
2) 每个vnode的计算获得的中间结果(partial results)需要进行第二阶段的聚合才能形成最终结果,第二阶段的聚合过程在客户端完成。
3) 由于表标签信息存储在vnode中, 因此针对标签信息的查询也需要vnode完成。客户端将标签的过滤表达式封装在查询请求结构体中发送给vnode, 由vnode的查询执行线程从中抽取出标签查询条件, 然后执行查询。标签查询与过滤是在针对表的查询之前完成。标签查询完成以后, 将符合条件的表纳入到接下来的查询处理流程中。
- 由于多表可能分布在不同的节点(dnode), 因此多表的聚合查询需要首先获得表所在的全部数据节点的信息, 并且同时向相关的dnode发出查询请求。
- 每个vnode的计算获得的中间结果(partial results)需要进行第二阶段的聚合才能形成最终结果,第二阶段的聚合过程在客户端完成。
- 由于表标签信息存储在vnode中, 因此针对标签信息的查询也需要vnode完成。客户端将标签的过滤表达式封装在查询请求结构体中发送给vnode, 由vnode的查询执行线程从中抽取出标签查询条件, 然后执行查询。标签查询与过滤是在针对表的查询之前完成。标签查询完成以后, 将符合条件的表纳入到接下来的查询处理流程中。
5.4 预计算
### 预计算
为有效提升查询处理的性能, 针对物联网数据的不可更改的特点, 在数据块头部记录该数据块中存储数据的统计信息: 包括最大值、最小值、和。我们称之为预计算单元。如果查询处理涉及整个数据块的全部数据, 直接使用预计算结果, 完全不需要读取数据块的内容。由于预计算数据量远小于磁盘上存储的数据块数据的大小, 对于磁盘IO为瓶颈的查询处理, 使用预计算结果可以极大地减小读取IO压力, 加速查询处理的流程。预计算机制与Postgre SQL的索引BRIN( block range index) 有异曲同工之妙。