TimescaleDB是一个针对时序数据的开源分布式数据库。它的目标是兼具NoSQL数据库的天然扩展能力和传统关系型数据库的可靠性与查询支持。它的核心概念主要包括Hypertable(超表)和Chunk(块)。底层存储架构在postgresql上。 作为一个postgresql的扩展提供服务。以插件化的形式,随着PostgreSQL的版本升级而升级,不会因为另立分支带来麻烦。
时序数据是随时间不断产生的一系列数据,例如持续监控的气象变化数据、股市交易记录、应用监控数据等,通常一个时序数据点可以由时序指标(metric)+ 一组标签(tags)+ 数据产生时间(timestamp)三部分唯一确定。
时序数据更适合体现数据“变化”的过程价值。如果在时间坐标中将这些数据点连成线,过往的数据可以形成多纬度报表,揭示数据的趋势性、规律性,捕获异常;未来的数据可以建立数据模型、做统计分析,实现预测和预警。
业务方对时序数据通常有几个查询需求。
获取最新状态,查询最近的数据(例如传感器最新的状态)。
展示区间统计,指定时间范围,查询统计信息,例如平均值,最大值,最小值,计数等。
获取异常数据,根据指定条件,筛选异常数据。
用户在TimescaleDB中主要通过Hypertable与数据交互。它是一个在所有的空间和时间上连续的抽象的表,可以通过标准SQL进行查询。实际上,Hypertable是许多实际存储数据的单个表的抽象或虚拟视图,它们称为chunks(块)。
通过将hypertable的数据划分为一个或多个维度来创建chunk。所有hypertable都由属于时间列的值进行分区,时间列可以是时间戳、日期或各种整数形式。若时间分区间隔为1天,则时间戳属于同1天的行位于同1个chunk内,不同天的行位于不同的chunk。
Hypertable也可以按附加列来分区(设备/服务器/用户id、位置等)。通常这种附加列上的分区使用hash散列(将数据映射到特定数量的hash buckets中),不过也可以使用基于间隔的分区。这种按时间和附加维度进行分区的hypertable被称为“时空分区”。
每个chunk都是用标准数据库表实现的。在PG中,chunk实际上是hypertable的“子表”。Chunk包括了分区范围的约束(如时间间隔范围),空间分区也会反映为chunk约束,因此hypertable中所有的chunk在空间上都是不重叠的。
插入hypertable的行会根据这些时空维度而“路由”到对应的chunk中。对于查询,只会将命令推送到适当的chunk去执行,排除掉与之无关的chunk(提高查询速度)。这部分操作对用户是无感的,用户只需要用标准SQL语句操作hypertable。
1、时间分区:
当创建和使用超级表时,它会自动按时间对数据进行分区, 并可选择按空间分区
每个超级表都由称为块的子表组成。分配的每个块 时间范围,并且仅包含该范围的数据。默认情况下,每个区块包含 7 天,可以通过chunk_time_interval来进行设置。分布式超表默认使用空间分区。
2、空间分区:
建议对分布式超级表使用空间分区,以实现高效的横向扩展性能。对于仅存 在于单个节点上的常规超表,额外的分区可用于特殊用例,不建议大多数用户使用
空间分区使用散列:每个不同的项目都被散列到 N 个存储桶之一。请记住,我们已经在使用(灵活的)时间间隔来管理块大小;空间分区的主要目的是在同一时间间隔内跨多个数据节点(在分布式超级表的情况下)或跨多个磁盘(在单节点部署的情况下)实现并行化
压缩时序数据可以进一步减少区块大小 超过90%。这样可以节省存储成本,并使查询保持运行 闪电般的速度。
支持按照时间进行自动压缩和手动压缩和解压,在查询期间,未压缩的块将被正常处理,而来自压缩块的数据将在查询时首先被解压缩并转换为标准行格式
可帮助您通过删除旧数据来节省存储成本。您可以 将数据保留与连续聚合相结合,以降低您的采样数据。
在时间序列应用程序中,随着数据年龄的增长,数据通常变得不那么有用。如果 您不需要您的历史数据,您可以在达到一定数据后将其删除 年龄。Timescale 允许您设置自动数据保留策略以丢弃旧数据。你 还可以通过手动删除块来微调数据保留。
通常,您希望保留历史数据的摘要,但不需要 原始数据。您可以通过将数据保留与连续聚合相结合来对旧数据进行缩减采样。
通过将数据分层到低成本对象存储层(S3)来节省存储成本
支持PostgreSQL提供的相关功能
TimescaleDB支持复制和副本功能,可以在多个节点上创建超级表的副本,实现数据冗余和高可用性
TimescaleDB 的多节点安装可以高度可用 通过为群集中的每个节点设置一个或多个备用节点,或者通过 在区块级别本机复制数据。
性能分析
总之,时序数据库适用于任何需要处理时间序列数据的场景,它可以帮助企业更好地管理和分析数据,提高业务效率和决策能力。
InfluxDB 是一种开源分布式时序数据库,时序数据库通常被用在监控场景,比如运维和 IOT(物联网)领域。这类数据库旨在存储时序数据并实时处理它们。使用 Go 语言编写,无需外部依赖。
InfluxData选择从头开始构建InfluxDB以支持下一代时序中台的需求,InfluxDB通过实现高度可扩展的数据接收和存储引擎,可以高效地实时收集、存储、查询、可视化显示和执行预定义操作。
它通过连续查询提升查询效率和缩短延迟,通过数据保留策略,及时高效地删除过期冷数据,提升存储效率。
为什么通用数据库在时序场景上不是最优的选择呢?许多通用数据库正在为时序数据添加一些支持,虽然可能很容易使用,但它们基本上都不是针对海量时序数据的吞吐量和实时操作而设计的。
与InfluxDB相比,通用数据库,如Cassandra、MongoDB、HBase等,需要开发人员投入大量的时间进行代码编写,以开发与InfluxDB类似的功能。具体来说,开发人员需要做如下工作:
编写代码实现跨集群数据分片功能、聚合运算和采样功能、数据生命周期管理功能等。
实现丰富的API接口。
编写用于数据采集的工具。
实现实时处理模块并编写用于监控和警报的代码。
编写可视化引擎以向用户显示时序数据。
在 InfluxDB 中,我们可以粗略的将要存入的一条数据看作一个虚拟的 key 和其对应的 value(field value),格式如下:
cpu_idle,host=k0614v,region=us-west value=83.13 1524730697000000000
虚拟的 key 包括以下几个部分: database, retention policy, measurement, tag sets, field name, timestamp
,database和retention policy在上面的数据中并没有体现,通常在插入数据时在 http 请求的相应字段中指定。
database
InfluxDB 中可以创建多个数据库,不同数据库中的数据文件是隔离存放的,存放在磁盘上的不同目录。
retention policy
存储策略,用于设置数据保留的时间、集群中存放副本数量以及shard group覆盖的时间范围。每个数据库刚开始会自动创建一个默认的存储策略 autogen,数据保留时间为永久,副本数量为1,shard group持续时间为7天,之后用户可以自己设置。InfluxDB 会定期清除过期的数据。
measurement
measurement作为tags,fields和time列的容器,measurement也是存储在相关字段中的数据的描述
tag sets
tags 在 InfluxDB 中会按照字典序排序,不管是 tag key 还是 tag value,只要不一致就分别属于两个 key,例如 host=k0614v,region=us-west 和 host=k0615v,region=us-west 就是两个不同的 tag sets
field name
例如上面数据中的 value 就是 fieldName,InfluxDB 中支持一条数据中插入多个fieldName,这其实是一个语法上的优化,在实际的底层存储中,是当作多条数据来存储。
timestamp
每一条数据都需要指定一个时间戳,在 TSM 存储引擎中会特殊对待,以为了优化后续的查询
Point由时间戳(time)、数据(field)、标签(tags)组成。
Point相当于传统数据库里的一行数据,如下表所示:
所有在数据库中的数据,都需要通过图表来展示,而这个series表示这个表里面的数据,可以在图表上画成几条线:通过tags排列组合算出来。
shard是在 tsm 存储引擎之上的一个概念。在 InfluxDB 中按照数据的时间戳所在的范围,会去创建不同的 shard,每一个 shard 都有自己的 cache、wal、tsm file 以及 compactor,这样做的目的就是为了可以通过时间来快速定位到要查询数据的相关资源,加速查询的过程,并且也让之后的批量删除数据的操作变得非常简单且高效。
每个shard有且只有一个shard group。 单个shard group中可能存在多个shard。 每个shard包含特定的series集合。
Shard duration决定了每个shard group的时间跨度。具体由retention policy的 SHARD DURATION
决定
shard group按照time、retention policy进行组织。每个包含数据的retention policy至少有一个关联的shard group。给定的shard group包含时间区间内所有shard数据。 每个shard group跨越的间隔是shard duration。
CQ 是预先配置好的一些查询命令,定期自动执行这些命令并将查询结果写入指定的 measurement 中,这个功能主要用于数据聚合。
InfluxDB存储引擎看起来非常类似于LSM树。它主要由cache、wal、tsm file、compactor组成
cache是当前存储在WAL中的所有数据的内存副本。point由metric,tag和唯一字段组成。每个字段保持其自己的时间有序范围。cache中的数据未被压缩。对存储引擎的查询将合并Cache和TSM中的数据。
当influxDB启动时,会遍历所有的wal文件,重新构造cache,这样即使系统出现故障,也不会导致数据的丢失。
wal的内容与cache相同,其作用就是为了持久化数据,当系统崩溃后可以通过wal文件恢复还没有写入到tsm文件中的数据。
由于数据是被顺序插入到wal文件中,所以写入效率非常高。但是如果写入的数据没有按照时间顺序排列,而是以杂乱无章的方式写入,数据将会根据时间路由到不同的shard中,每一个shard都有自己的wal文件,这样就不再是完全的顺序写入,对性能会有一定影响。官方社区有说后续会进行优化,只使用一个 wal,而不是为每一个shard 创建 wal。
wal单个文件达到一定大小后会进行分片,创建一个新的wal分片文件用于写入数据。
TSM文件是内存映射的只读文件的集合。 这些文件的结构看起来与LevelDB或其他LSM树变体中的SSTable非常相似。
一个TSM文件由4个区域组成:header,blocks,index,footer
┌────────┬────────────────────────────────────┬─────────────┬──────────────┐
│ Header │ Blocks │ Index │ Footer │
│5 bytes │ N bytes │ N bytes │ 4 bytes │
└────────┴────────────────────────────────────┴─────────────┴──────────────┘
┌───────────────────┐
│ Header │
├─────────┬─────────┤
│ Magic │ Version │
│ 4 bytes │ 1 byte │
└─────────┴─────────┘
0x16D116D1
1
┌───────────────────────────────────────────────────────────┐
│ Blocks │
├───────────────────┬───────────────────┬───────────────────┤
│ Block 1 │ Block 2 │ Block N │
├─────────┬─────────┼─────────┬─────────┼─────────┬─────────┤
│ CRC │ Data │ CRC │ Data │ CRC │ Data │
│ 4 bytes │ N bytes │ 4 bytes │ N bytes │ 4 bytes │ N bytes │
└─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
Blocks 是一系列crc32和数据块组成。crc32可以用来恢复,确保data的完整性。block的长度保存在索引中。
┌────────────────────────────────────────────────────────────────────────────┐
│ Index │
├─────────┬─────────┬──────┬───────┬─────────┬─────────┬────────┬────────┬───┤
│ Key Len │ Key │ Type │ Count │Min Time │Max Time │ Offset │ Size │...│
│ 2 bytes │ N bytes │1 byte│2 bytes│ 8 bytes │ 8 bytes │8 bytes │4 bytes │ │
└─────────┴─────────┴──────┴───────┴─────────┴─────────┴────────┴────────┴───┘
索引由一系列索引条目组成,先按照 key 的字典序排序,再按照 time 排序。 每条索引都以key len、key开始,count是文件中块的数量。
以下部分为block索引,根据count数量重复出现。
┌─────────┐
│ Footer │
├─────────┤
│Index Ofs│
│ 8 bytes │
└─────────┘
最后是Footer,它主要是存放索引的开始位置。
compactor是一个持续的过程,优化存储,提升查询性能,具体做以下几件事:
将已关闭的WAL文件转换为TSM文件并删除已关闭的WAL文件
将较小的TSM文件合并为较大的文件以提高压缩率
删除series data
写入最新的数据,确保TSM文件中point的唯一性。
centos虚拟机
cpu: AMD Ryzen 5 1600 Six-Core(4线程)
内存:8G
磁盘:机械硬盘
版本:PostgreSQL 10.4+TimeScaleDB 0.10
shared_buffers = 2g (推荐设置为机器内存的1/4,默认值为1g)
wal_buffers = 64m (推荐设置为shared_buffers的1/32,默认值为16m)
wal_writer_delay = 800ms(默认值200ms)
除上述修改之外均使用默认配置
版本:1.6.0
cache-max-memory-size = “2g” (默认值为1g)
cache-snapshot-memory-size = “100m”(默认值为25m)
[http] log-enabled = false
wal与data目录指定不同磁盘
除上述修改之外均使用默认配置
使用JAVA代码编写InfluxDB和TimeScaleDB的写入程序,在本地运行测试程序读写服务器的数据库,写入时batchsize为5000,读取数据时全表扫描读取
batchsize取用5000的原因是:使用上述表结构测试,TimeScaleDB和 InfluxDB的写入batchsize逐渐提升的过程中,当batchsize接近5000时性能有明显提升,当batchsize大于5000时InfluxDB性能下降明显,TimeScaleDB性能有微小提升。综合考虑后将batchsize设置为5000进行测试。测试数据如图:
总行数:1亿行
总数据量:18.1G
单行大小:95byte
TimeScaleDB:使用默认的时间分区跨度:1个月 ,所有数据都将写入同一个分区
InfluxDB:使用默认的时间分区跨度:7天,所有数据都将写入同一个分区
由于InfluxDB的时间戳是有索引的 ,因此测试TimeScaleDB时在时间戳字段创建了btree索引。
单并发
单并发写入的情况下,InfluxDB的写入性能约为TimeScaleDB的2倍,写入数据的过程中,TimeScaleDB内存消耗明显高于InfluxDB。
多并发
4并发写入,InfluxDB的性能表现依旧比TimeScaleDB好,将近2倍写入性能,资源消耗差异不大。
以上测试数据,InfluxDB存储空间占用不到TimeScaleDB的一半。(原始数据量18.1G)
参考链接:
https://blog.csdn.net/gongpulin/article/details/81023085
https://blog.csdn.net/suzy1030/article/details/81478514