将哪个数据模型/模式应用于具有不同字段的数据源的时间序列数据存储

K. *_*eck 8 schema postgresql database-design time-series-database timescaledb

我被要求为时间序列数据开发数据存储,尽管进行了大量研究,但我不确定要选择的数据模型和存储技术。

关于数据

要存储在数据存储器中的源数据由物理测量单元提供。每个单元可能有也可能没有不同的变量子集,每个测量站有多达 300 个变量(例如燃料类型、燃料消耗、速度),而所有站的不同信号数量约为 1500。预先知道每个站的预期变量子集。但是,随着时间的推移,可能会向站点添加额外的传感器(随着时间的推移,可能需要更改架构)。所有站都以从 20Hz 到 0.2Hz 的不同速率提供数据。

此外,还有相当数量的元数据可供所有这些测量站使用,最终我们将拥有大约 500 个。

数据通常是批量输入的,而不是“实时”流。批次大小从每小时批次到每月批次不等。

关于查询

进行数据查询主要有两个原因,单测站数据的上报和统计分析,以及跨站比较。大约 80% 的查询与过去 30 天内输入的数据有关。查询每天进行,因此SELECT负载超过INSERT负载。

理想情况下查询像

SELECT var1, var2, ... varN FROM station_data WHERE station_id=X OR station_id=Y AND TIMESTAMP BETWEEN ... AND ...;
Run Code Online (Sandbox Code Playgroud)

非 SQL 专家可以轻松访问数据。此外,简单的基于时间的聚合算法应该是可能的(AVG、MAX 等 pp)。

现在的情况

目前,使用高度规范化的结构将数据存储在 PostgreSQL 数据库中,该数据库现在增长到大约 6TB,每个变量一个表。大约 1500 个数据表中的每一个都是这样的形式

(timestamp, station_id, value)
Run Code Online (Sandbox Code Playgroud)

索引(station_id), (station_id, timestamp), (timestamp)和唯一约束(station_id, timestamp, value)

这种结构需要大量的外部连接(最多 300 个外部连接),这使得数据检索变得繁琐且计算成本高。

研究

到目前为止,进行了以下考虑:

数据库技术

  1. 虽然 NoSQL 将提供所需的架构灵活性,但确保数据完整性、访问控制和元数据管理的工具似乎具有挑战性,并且内部不存在 NoSQL 经验。此外,阅读与此相关的评论和答案似乎有利于我们用例的 SQL 解决方案。
  2. 不同的时基优化数据库被认为是(主要CrateDBTimescaleDB)。两者在“自动”分区和分片方面看起来都很有希望,其中 TimescaldeDB 会因为基于 PostgreSQL 而受到轻微青睐。

数据模型/模式

到目前为止,已经制定了两种不同的模式,它们在原则上是有效的。但是,两者都有明显的缺点,我需要找到解决方法。

  1. EAV(反)模式与一个巨大的垂直数据表,分片station_id和每月分区timestamp。虽然将提供所需的模式灵活性,但这种模式不符合所需的易于访问的要求,因为它仍然严重依赖于内部联接。此外,在 db 端无法确保不同数据类型的类型安全,并且无法进行访问控制。
  2. 每个表station_id带有在将传感器添加到特定站时水平变化的模式。从应用程序的角度来看,这种非规范化的结构乍一看很有吸引力(快速插入、几乎不需要索引、单站上的简单查询)。但是,查询将需要动态 SQL,因为最终用户可能不知道特定站点的表名,并且只有使用扩展 SQL 查询或客户端代码才能进行跨站点比较。

一般注意事项

虽然存储容量不是问题,但数据检索的可靠性、正常运行时间和速度才是问题。

为了在保持可扩展性的同时满足要求,建议的数据模型中的哪一个是首选?非常欢迎对符合要求的任何其他模式的建议。

谢谢你。

Dzh*_*dzh 1

除了变量数量的变化之外,我的数据也有非常相似的情况,但正如TmTron所说,JSON 可能适合你。这是我的架构(适应您的数据):

表“传感器:包含您想要的任何元数据,定期约 1k+ 行,在某些情况下 7k+ 没有实际差异。

表“传感器数据”:

  • 时间戳,
  • sensor_id int, -- FK 到传感器
  • measurement_id int (我有 14),
  • var1,var2,var3,var4,var5 --对我来说它是一组 5 个 int8,在你的情况下它是不可列的数据,比方说 JSON
  • 索引(sensor_id,measurement_id,timestamp)(大约是表大小的1/3)

大量的查询,例如

{select timestamp, var1,var2,var3,var4,var5 from sensor_data where sensor_id = xx and timestamp between xxxx and xxxx}
Run Code Online (Sandbox Code Playgroud)

表变大、查询变慢、顾客更生气等等。

第一次优化尝试是按 Sensor_ids 范围进行分区 - 每个分区 20 个,空间消耗保持不变,模式变得更加复杂,查询变得更快,但没有那么多。

所以,这里仍然是工作模式:

自定义数据类型“metric”(时间戳、var1、var2、var3、var4、var5)

表传感器数据:

  • 日期
  • 传感器 ID
  • 测量ID
  • 数据集 - 它是“metric[]”类型的列 - 包含日期唯一索引的所有数据的数组,按日期、sensor_id、measurement_id

选择查询已替换为函数 get_data(sensor_id,measurement_id,from_time, to_time) select (unnest(dataset)).* fromsensor_data 其中sensor_id = xx 以及 from_time::date 和 to_time::date 之间的数据以及measurement_id = xxx

插入变得更加复杂:

insert into sensor_data value (to_date(timestamp), sensor, measurement, [(timestamp, var1,var2,var3,var4,var5)])
on conflict (date, sensor_id, measurement_id) do update
set dataset=dataset||excluded.dataset
Run Code Online (Sandbox Code Playgroud)

空间消耗减少约 10 倍,查询更加复杂,但速度显着加快。

如果您不通过measurement_id请求数据,只需将其从索引和查询中删除即可。如果您每天有更多的数据,您可以每小时存储数据,并将“日期”列替换为“小时”作为date_trunc('hour',timestamp)每月的分区表,这样每个传感器的每个测量最多有 744 (31*24) 行桌子。这是相当合理的行数并且工作速度足够快。

显然你必须编写自己的数据类型(对于大多数情况,类型(时间戳,JSON)都可以)

主要思想是 postgres 将数据数组存储在表之外,并且仅在需要时才读取它们(而且它是压缩的)。因此,表成为存储在其他地方的数据的“有点索引”,但仍然是可以索引和分区的表。

限制是您无法通过约束控制数据集数组内容并直接聚合数据。但对于简单的聚合(如最大值、最小值、平均值),您可以预先聚合数据并仍然将其存储在行级别。