Fastdata for TSDB: SQL 使时序数据可扩展
作者: 乔峰
1. 背景
时间序列数据出现在越来越多的场景,监控和运维,传感器数据和物联网,金融数据,物流数据,应用程序使用数据等等。通常这些数据有着体积大,复杂性高的特性(如:多个测量量和标签关联一个时间点 )。这就意味着,存储时间序列数据满足规模和复杂查询两方面的要求。然而,很难同时实现这两个属性。用户通常面临着水平可伸缩的 NoSQL 数据库与拥有强大查询能力的关系型数据库之间的权衡。
TSDB 是一个时序数据库,被设计使时序数据 SQL 可扩展。在一个通常被分为 RDBMS 和 NoSQL 数据库的世界里,Fastdata for TSDB 提供了第三种选择,结合了两者优点:一个集群扩展架构和对复杂查询的支持。通过透明的自动执行时空分区和查询优化,TSDB 支持高提取率的水平扩展,同时允许用户和数据互动,就好像这些数据在一个单独且连续的表中。通过执行高效的索引和优化来选择和聚合非主键,它支持快速查询。数据可以实时查询,避免了批量加载数据的写入延时。此外,因为 TSDB 是基于 PostgreSQL 开发,它提供了完整的 SQL 接口(包括对二级索引和连接的支持),同时继承了 PostgreSQL 的可靠性,成熟的生态系统和操作易用性。随着数据被测量,时间变成了一个更重要的维度。TSDB 允许开发人员和组织利用时间的力量:分析过去,认识现在和预测未来。在查询水平统一时序数据和关系数据可以排除数据仓库,使延时和原型更容易取得进展。可扩展性和完整的 SQL 接口的结合使得一个组织里各种各样的人员直接向数据提问。换句话说,通过支持已经广泛使用的查询语言,TSDB 确保了你的问题只受限于你的想象力,而不是数据库。
关键的挑战:时序数据库的一个中心目标就是支持 高写入率,许多这样的典型应用都是跨行业的。例如:在物联网场景中,不管是工业、农业、城市还是消费者领域,由于有大量的设备,再加上每个设备适度的高写入率,造成的整体的高写入率。在物流场景中,计划数据和真实数据都包含了可以和每个追踪对象有关的时间序列。应用监视场景下,如:DevOps,会追踪每个系统组件的许多指标。
在多种形式的金融应用中,例如那些基于标记的数据,也依赖于时间序列数据。所有这些都需要一个高容纳率的扩展。此外,除了简单地获取或聚合一个特定时期的单个指标,这些应用程序通常还要使用复杂和任意的方式查询数据。这样的查询模式可能包含丰富的谓语(如:WHERE 字句中复杂的连词)、聚合、统计、窗口操作、连接等依赖于关系数据的操作。相比与创建一门新的查询语言(这需要开发人员和分析师提供新的培训,以及新的客户接口或者连接器用来和其他系统整合,一个理想的时序数据库应该支持标准的 SQL,并且暴露一个全局的抽象表,即使底层存储可能被分区或分片在多个服务器或磁盘之间),这样的设计允许人们操作数据,就好像数据存在于一个标准的表中,隐藏了数据分区和用户查询优化的复杂性。
我们如何扩展 SQL?乍一看,水平扩展且高效的 SQL 数据库的说法似乎值得怀疑;毕竟整个 NoSQL 运动有没有摆脱 SQL 数据库的局限性?这一运动应对 SQL 数据库在传统事务性的工作负载上的使用。在联机事务处理(OLTP)中,一般的事务更新会涉及多行已存在的数据,如从一个银行账户借钱的同时向另一个授信,以一种原子的方式;在两个关键的方面,时序的工作负载是不同的。
(1)时序数据是不可变的。新数据的不断到来,通常对应着新的时间周期。换句好说,写操作的发生伴随着新的插入,而不是更新已存在的行。 此外,数据库需要同时支持回填延时数据,写操作主要有最近的时间间隔产生。
(2)工作负载本质的区分时间和空间。写操作通常产生在最近的时间间隔,穿过空间维度的“分区键”(如:数据来源,设备,用户等等)。查询通常是一个指定时间序列或数据来源或许多数据源在一定时间间隔内的问题。然而查询可能不嫌疑一个特定的指标,但是可能定期的一次选择多个指标(或在多个指标上使用谓词)。这样的工作打开了数据库架构可能性新的空间,这正是 TSDB 的优势所在。值得注意的是,不仅是这些不同于传统联机事务处理工作的特性,也包括联机分析处理(OLAP)聚焦于读的负荷和单个指标的汇总和聚合。
2. 现有方案的局限性
现有方案需要使用者在可扩展性和丰富的查询支持之间选择。常见的的 RDBMS(如:PostgreSQL、MySQL)。传统 SQL 数据库在处理高容量数据时有两个关键问题,对大量表格的写操作性能太差,这个问题会随着时间的推移,数据体积的线性增长而变得更加糟糕。表索引无法装入内容时,这些问题就会出现,每个插入将会转换为许多磁盘读写用来交换 B-Tree 索引的部分。此外,删除任何数据(为了节省空间或实现数据保留策略)都将需要昂贵的 vacuuming(清理)操作以整理和这些表相关的磁盘存储。此外,依然缺少扩展 RDBMS 的开箱即用的解决方案。
NoSQL 和时序数据库。作为回应,开发人员有时会采用 NoSQL 数据库(如:Cassandra,Mongo)或者现代化的时序数据库(如 OpenTSDB,InfluxDB)来满足他们的需求。通常这些基于列的数据,为了在列上进行快速的提取和快速的分析查询,会把暴露一个更简单的键-值接口。依赖于数据模型的选择,对于展示一个单一的指标(如:一个设备的 CPU 利用率)或者多个聚合的指标(所有设备的平均 CPU 利用率),这都很奏效。但是,他们往往缺乏丰富的查询语言或二级索引支持,在复杂的查询上会造成很高的延迟。例如:当自定义查询的 WHERE 字句中包换一个数字列时, InfluxDB 就会回滚进行全表扫描。String 类型的列对大多数 NoSQL 时序数据库造成了挑战。例如,一个索引字符串列(如:ip_address,uuid,event_name)的方法使用所有索引值连接而成的字符串作为一个键。这将会在索引字段的数量和性能(搜索一个字段,需要扫描所有其他的索引字段)之间做出权衡。这些问题是多字段查询(如:某类型设备低电量的所有指标)变得低效。类似这样的查询在仪表盘、报告和数据浏览时很常见,此外,它们缺少可靠性,工具,还有广泛使用的传统数据库系统的生态系统。
数据池(如:SQL/Hadoop/Spark on HDFS)类似 HDFS 的块/文件系统避免了预定义数据模型或结构的需要,更容易通过增加服务器扩大规模。它们在大体量、不可变的批量数据处理上拥有更高的写入速率。然而,它们在查询时间上付出了代价,取法高度结构化的索引用来进行快速和节省资源的查询。此外,考虑到底层存贮在不可变的块中,它们的数据回填和更新十分困难和昂贵。
所以,虽然 HDFS 之上的查询引擎可以暴露一个 SQL 或者丰富的编程接口(例如:Presto,Pig,SparkSQL 等),许多类型的查询都可以被拥有合适索引支持的 RDBNS 系统以体层春绸接口全表扫描的方式高效地处理。虽然 HDFS 允许这种扫描很容易并行,但对查询延时和吞吐量依然有显著的影响。因为这种架构更多的为单租户数据浏览和草稿生成设计,而不是仪表盘或者实时操作监控。
3. TSDB 内核架构 TSDB 是一个针对时序数据的数据库。
它的目标是兼具 NoSQL 数据库的天然扩展能力和传统关系型数据库的可靠性与查询支持。为此,我们已经设计了一个新的集群 DB,围绕 PostgreSQL 核心进行构建,并运行在每个服务器上。TSDB 支持现代时序数据库的关键特征,如图 1 中归纳的那样。Hypertables and Chunks(超表和块)。在一个更高的水平,数据库暴露了一个抽象的连续的表格:一个元表格,横跨所有的空间和时间间隔,这样你就可以通过普通的 SQL 语句查询它,一个元表格使用标准的带有列名称和类型的模型,在集群部署环境中,一列指定了一个建立在可额外分割的数据集上的分区键。
图 1:TSDB 内核的关键特征
在内部 TSDB 自动将元表分割成块,一个快对应着一个根据指定时间间隔和此分区键的区域确定的二维空间。每个块使用表中数据库表实现,这个表自动地被放置在某一个数据库节点中(或者在多个节点间复制),虽然这个细节很大程度上被隐藏。一个单一的 TSDB 部署可以存储多个元表格,每个表格可以有不同的表结构。构建于 PostgreSQL 之上。选择了构建于 PostgreSQL 之上,而不是从零构建,TSDB 获得的四个直接的收益。
1.坚如磐石般的可靠性。在其核心,TSDB 的可靠性来自于 PostgreSQL 20 余年的开源记录和简答的开发者社区。
2.成熟的生态系统。TSDB 用户可以通过标准 ODBC、JDBC 或者 PostgreSQL 连接第三方可视化工具、BI 工具,接口管理、网络平台和 ORM。
3.标准接口。TSDB 用户不需要重新学习一门新的查询预演和管理框架,可以利用现有的 SQL 语句和 PostgreSQL。
4.操作使用简易。用户可以重用已知的可信赖的方法来备份,快照,活跃复制以及进行其他操作。
实际上,所有的 TSDB 实现都是在 Postgres 之上的扩展,而不是一个拷贝。所以,TSDB 可以安装在一个标准的 PostgreSQL 上。
当你考虑替代方案时。数据库设计通常需要深思熟虑的权衡,如果不提及也许存在的更好的替代方案,那将会是我们的疏忽。
简单的读需求:当你的大部门查询模式本质上是很简单的(如:基于键的查询,或者在时间这一维度上的汇总)。
低可用存储:当资源受到约束,存储十分珍贵时,高的压缩率就成了必须。(虽然这是一个积极开发的区域,我们也期待 TSDB 改善)
稀疏的或者非结构化的数据:当你的时序数据非常稀疏,或者一般是非结构化的。(即使你的数据使部分结构化的,TSDB 包含了准对非结构化数据的 JSONB 字段类型。这允许你结合非结构化的存储维护你数据中的结构化部分的索引)
4. 技术细节
理想情况下,数据库用户应该可以和时序数据交互,就好像数据存在于一个简单连续的数据库表中。然而,基于以上讨论的原因,使用一个单一的表不能规模化。但是,需要用户手动分区会暴露许多复杂性,如:强制用户不断指定查询那个分区,如何计算他们之间的连接,或者如何随着负荷变大恰当地测量这些表。为了避免这种同时管理复杂性,同时扩展和支持高校的查询,TSDB 隐藏了 hypertable 抽象背后的自动数据风趣和查询优化。使用一个简单的标准 SQL 命令(见图 2)就可以创建一个 hypertable 及其相应的表结构,并且这个 hypertable 可以像一个单独的表那样使用标准的 SQL 命令(见图 3)进行访问。而且,如同一个正常的数据库表,它的表结构可以通过标准的 SQL 命令进行改变,这些对用户都是透明的,TSDB 会原子的修改其之下的包含 hpertable 的块的表结构。
图 2
图 3
查询示例使用标准的 SQL 和一个单一全局表的抽象。请注意,数据分区伸缩的复杂性对用户是隐藏的。通过在 PostgreSQL 查询计划中插入钩子,接收原生 SQL 解析树,TSDB 提供了这个功能。然后,他就可以使用这棵树来确定那个访问哪个数据库和超表块(原生数据库表),如何执行分布式和并行优化等等。许多相同的优化甚至适用于单点部署,在自动将超表分割成块和相关查询优化的地方提供了大量的性能优势。本节的其余部分,我们着重强调和解释 TSDB 的架构和设计选型。
4.1 规模扩展
透明的时间/空间分区。如前所述,数据库通过两个维度的分区元数据表进行扩扩展,建立在一些主索引之上的时间间隔和分区键(如:传感器数据中的设备标识符,位置,客户、用户等)每个时间/空间被称为一个块。被系统自动创建和放置在一个服务器或者磁盘中。TSDB 的架构示意图见图 4。
图 4
图 4 每个数据库节点都知道包含在每个块中的数据的时间和空间范围,每个节点建立每个单独块的本地索引。如我们后面讨论的那样,当解析一个查询时,特别适当 WHERE 或者 GROUP BY 子句包含时间空间(分区键)维度时,块的范围信息允许查询规划器决定查询那个块(这通常被称为约束排除分析)。本地索引可以在任何数据库的列上创建,不局限于主分区键和被定义为时间和被索引列的连接上。
如:在图 2 中展示的超表中,如何分区键是设备 ID,二级索引被定义为位置或者其他用数值表示的传感器读数(问题,湿度,颗粒物)。通过在这些列和时间上定义本地索引,TSDB 规划器就会知道如何优化查询谓语中给定时间的查询。
横跨块和服务器的并行化。块在运行时被动态的创建和调整大小已优化在集群和单点环境下的性能。当作为一个集群运行时,块被放置在不同的服务器上,在一个单独的机器上,块也会配自动地分配在磁盘中,不管是哪种方式,通过平行空间的分区都会插入到最近的时间间隔中。同样,查询模式通常也会切分时间或者空间,这样就能享受到之恩能够块存放带来的性能改进。默认情况下,块属于分区秘钥空间的同一区域,通过时间间隔区分,在统一服务器上并列。当对单一对象在空间进行查询时,避免了接触所有的服务器,有助于坚守高查询负载下的尾延时。
适合单节点的块大小。即使在但节点配置下,块已让对单一数据库标的插入和删除操作的使用提供性能改进。合适大小的块可以确保一个表索引的所有 B-tree 可以在插入操作发生,驻守在内存中,避免修改树中任意位置时发生抖动。此外,通过避免过于大的块,当根据自动保留策略移除被删数据时,我们可以避免代价不菲的清理操作。运行时可以通过简单的删除块(内部表)执行这样的操作,而不是删除一个一个的行。同时,无需从磁盘读取额外的表和序列,可以坚守很小的块,从而改善查询性能。TSDB 自动根据表大小进行时空分区,而不是更常见的基于静态的时间间隔分区(如:每天创建一个单独的表)。当系统缺少 TSDB 这种透明的超表抽象时,基于时间间隔的分许会进行手动的表选择和连接,这至少也是易于操作的。但是,这种这件间隔在数据体积变化时,就会工作的十分糟糕。如:一个从 100 个设备拉取数据的时间间隔,在系统规模扩大到 10 万个设备时就不合适了。TSDB 通过自动管理数据分区,避免了进行这个选择的必要。
4.2 高数据写入
批量提交。写操作通常在最近的时间间隔发生,而不是老的表格中。这允许 TSDB 高效地编写批量插入到一批小数量的数据库中,而不是执行许多小的写入操作。更进一步,我们的扩展设计充分利用了时序数据负载在最近时间间隔的优势,为了快多服务器或磁盘进行并行写入以进一步支持高速的数据写入。当使用 HDD 或者 SSD 时,这些方法能够提高性能。
内存索引。因为对服务器而言块总是合适大小的,因此数据库不会构建巨大的单一的表,TSDB 为最近的时间间隔(大量的写出现在这里)交换索引到磁盘中。然而,它支持为任何类型的 PostgreSQL 列建立索引,从传统的文本或者数值列,到专业的数组数据类型或者 GIS 列。
事务支持。TSDB 对相同分区键的条目具有完整的事务支持。在一个监视程序中,这可以确保每一个设备底层具有事务性语义,保证了可能涉及到多个独立传感器指标的多设备测量值被原子性的插入。支持数据回填。虽然 TSDB 的架构设计为大部分写操作发生在最近的时间间隔这一场景而优化,但他完全支持回填延迟数据。此外,数据库的自动化分块管理也意识到了数据回填的可能。只要被配置后不超过一个块的尺寸限制,适度数量的延时数据就可以被插入,而不用创建额外的较小尺寸的块。
单节点的性能收益。虽然数据分区在传统意义上被视为扩展规模到多台服务器的一种机制,TSDB 的方式还提供了了即使在单台机器上也有意义的性能提升。为了评估这种方式,我们进行了实验,多个客户端插入单独的行到数据库,也有多个客户端在一个操作中批量插入大量的行。每个航包含不同列中的 10 个值,包括是兼职和索引,随机选取得主键 ID,这样的批量插入在数据库中庸中是很常见的,特别是在大规模的生产环境中。比如,当从一个如 Kafka 那样的分布式队列中获取数据时。图 5 显示了结果。
图 5
图 5:TSDB(红色)的成块元表与 PostgreSQL(蓝色)中单独的表在一个数据库服务器上的插入吞吐量对比。实验使用 Azure standard DS4 v2(8 核)配置,拥有 SSD-based(premium LRS)存储。每个插入的行含有 10 列,趋势线构成贴近于潜在数据的多项式,每个数据点展示了以 10 秒为周期的平均插入吞吐量。
PostgreSQL 的吞吐量随着表格的增长而下降,而元表的插入性能依然恒定,不管总的数据体积如何变化。在这两种场景中,标准 PostgreSQL 表性能在千万行的数据量后出现悬崖式下降,不仅吞吐量下降,方差也显著增加。在单行插入中,数据库常常只能实现每秒钟数百行插入。在批量插入中,PostgreSQL 的插入速率只收敛与每个周期数千行插入,相比开始时,有一个 10 倍的性能下降。另一方面,TSDB 保持着常数级的插入性能和低方差,不管数据库的大小如何增长,作为单独的块保持着适当的大小。当批量插入式,TSDB 开始时的平均插入速率是 140,000 行每秒, 和 PostgreSQL 接近。但不像 PostgreSQL 那样,随着数据尺寸的增长,TSDB 保持了其稳定的性能。
4.3 优化复杂查询
智能地选择需要的块以更好的满足查询。通常对时序数据的查询包含: Ⅰ)给定一个对象(如:设备)的跨时间切片; Ⅱ)给定一个时间间隔的块对象切片;Ⅲ)在所有对象或一些特殊的数据标签中(或者其子集中)查询最新数据记录,这种对时间或空间的查询,可能需要扫描许多的块(磁盘、服务器),如图 4 所示。当用户执行这些查询时,就好像在操作一个单独的超表,TSDB 利用智能管理的元数据,只对那些可能满足查询谓语的块进行查询。通过积极的修剪和查询计划有关的块和服务器,TSDB 同时改进了查询延时和吞吐量。
减少‘LIMIT BY’查询特定项目的回扫时间。类似的,对于像特定的设备、用户或者地理位置,人们常常想要问这样的问题,“给我每个设备最后的读数”。最让证查询可以使用原生的使用窗口操作的 SQL 语句表达,但对大部分关系型数据库,这样一个查询会转化为一个全表扫描。实际上,这种全表扫描可以继续返回到捕获 ‘每个设备’或在一些任意指定的时间范围最大程序牺牲完整性。为了有效的支持这种查询,TSDB 自动地跟踪数据库中关于截然不同项目的元数据,像元数据表结构中指定的那样。再加上其他的优化,这样的查询只会触及必要查询的块,在每个独立的块上执行有效的索引查询。
最小化来自块的已排序数据。TSDB 提供了许多额外的查询优化,既有利于单点部署,也有利于集群环境。PostgreSQL 已经使能了一个合并追加优化,当组合来自多个块的有序数据时,在这些块中,一个有效的查询组合来自这些表中排好序的数据,以合适的顺序一行一行的添加到查询结果中,当全局查询被满足(如:根据 LIMIT)时停止。这种优化确保了子查询只有在结果有利于最终全局结果集时才会在一个表上进一步执行。这对含有复杂谓语的查询特别有益,如查找匹配包含大量数扫描谓语的下一个项目。TSDB 扩展了这种合并-追加优化,把这些有意义的效果带到了基于时间的聚合中。这样的聚合经常出现在时序分析中,例如,告诉我一个指标每小时的平均值,设备在过去 12 小时的上报数据, 表述为 SQL 语句时涉及到 “GROUP BY hour ORDER BY hour DESC LIMIT 12”,这种方式,即使用户没有严格的指定严格范围(不同与许多时序数据库),数据库只会处理这个查询所需数据和块的最小集合。
并行聚合。很像它的 LIMIT 下推表(后进先出表),TSDB 对许多常用聚合函数(如:SUM,AVG,MIN,MAX,COUNT)进行下推。主要有利于集群部署,这种分布式查询优化极大的减少了进行大量汇总或分组时的网络传输,所有只有及所过的结果需要被加入到最终的查询结果中,而不是来自每个块的原始数据。
4.4 利用 RDBMS 的查询规划器
因为每个节点都运行着一个成熟的 PostgreSQL 查询规划器和数据模型,部署针对特殊查询、索引和数据类型的新优化是很容易的。从一开始,TSDB 就支持完整 SQL 接口。在当前的实现中,一些查询会比另外一些得到更多的优化。然而,随着时间的推移,后续的发布将会包含更多的额外的查询优化,数据库也会随着用户的需求而不断成长,而这一切不需要用户的任何修改。
依靠关系型数据的连接。今天,既可以计算超表和标准数据库关系表的连接,它们直接存储在数据库中或通过外部数据封装器包装成外部数据库访问。 未来,加入的优化将会坚守连接期间的数据系统。大部分时序数据库都不支持这种 JOIN 操作,这种缺乏支持会使用户给每个时序数据记录存储额外的元数据或标签而造成数据不规范。这种方式数据尺寸极大的扩张,是更新元数据变得十分昂贵。另外,应用程序作者在存储他们数据库之间的数据,然后要求应用程序作者执行关系型数据和数据库外部的时序数据之间的连接,这增加了系统之间的复杂性。地理空间查询。TSDB 时序数据支持任意的 PostgreSQL 数据类型,包含了扩充 PostGIS 的 GPS 坐标数据,它提供了对地理空间数据一流的支持。因为一个块可以使用任意类型来索引他的数据,支持开箱即用的在 GIS 数据之建立 GIN/GiST 索引。
4.5 灵活的管理
因为块在内部是本地数据库表,所以我们可以容易地在 TSDB 中利用已有的 PostgreSQL 的能力。
工具生态。TSDB 利用 PostgreSQL 生态系统 20 年开发的管理工具和特性,用户可以通过 JDBC 或者 ODBC 连接器和命令行工具连接到 TimescaleDB。然而,监狱 TSDB 的分许实现,用户通常只能看到 hypertable,而看不到与他们共存的块。高可用(复制和备份)。TSDB 可以复用 PostgreSQL 久经沙场的复制技术,即流失复制和冷/热备用,同时还有备份。他还使用 PostgreSQL 的预写式日志(WAL)进行一致性检查。换句话说,即使复制或者备份策略可以被定义在 hypertable 上,TSDB 通过复制或者检查组成 hypertable 的块来执行这些操作。自动的数据保留策略。TSDB 允许轻松地定义基于时间的数据保留策略。例如:用户可以配置系统清理/擦除超过 X 周的数据。TSDB 基于时间间隔的分块也有助于这种保留策略更高效,因为数据库可以仅仅删除过期的内部数据表,而不是需要删除独立的行和侵略性的清理返回的表。为了效率,这些策略可以被懒实现。也就是说,超过有效期限的独立记录,可能不会立即被删除。相反,当一个块中的所有数据都失效时,真个块就会被删除。
5. 结论和现状
许多时序应用需要比以前更加复杂的数据分析:分析历史数据,监测当前行为,识别可能问题,预测未来行为等等。反过来,数据采集的存储量和速度都变得更大。为了服务这些应用,需要一个更加现代化的时序数据库来满足可伸缩性的要求,支持高性能的复杂查询。Fastdata for TSDB 通过自动地时空分区,查询规划优化和深度集成 PostgreSQL 实现了这些。同时得益于超表的抽象和完整的 SQL 支持,Fastdata for TSDB 通过一个易用的接口,提供了这一性能。
如果你对以上内容感兴趣且需要帮助的话,可以登录https://www.deepexi.com/product-new/27了解更多 TSDB 产品详情。
版权声明: 本文为 InfoQ 作者【滴普科技2048实验室】的原创文章。
原文链接:【http://xie.infoq.cn/article/51415b9aa6dff7c36543eea7f】。文章转载请联系作者。
评论