如何存储时间序列数据

gue*_*t82 25 postgresql architecture

我有我认为的时间序列数据集(如果我错了,请纠正我),它有一堆相关的值。

一个例子是对汽车进行建模并在旅途中跟踪它的各种属性。例如:

时间戳 | 速度 | 距离旅行| 温度 | 等等

存储这些数据的最佳方法是什么,以便 Web 应用程序可以有效地查询字段以查找最大值、最小值并随时间绘制每个数据集?

我开始了一种解析数据转储并缓存结果的幼稚方法,这样它们就永远不必存储。然而,在玩了一会儿之后,由于内存限制,该解决方案似乎无法长期扩展,如果要清除缓存,则需要重新解析和重新缓存所有数据。

另外,假设每秒跟踪一次数据,很少有可能出现 10 小时以上的数据集,是否通常建议每 N 秒采样一次来截断数据集?

Chr*_*ris 33

确实没有一种“最佳方式”来存储时间序列数据,老实说,这取决于许多因素。但是,我将主要关注两个因素,它们是:

(1) 这个项目有多严重,值得你努力优化模式?

(2)你的查询访问模式真的会是怎样的?

考虑到这些问题,让我们讨论一些模式选项。

平桌

使用平面表的选项与问题(1)有更多关系,如果这不是一个严肃或大规模的项目,您会发现不考虑太多架构会容易得多,并且只需使用平面表,如:

CREATE flat_table(
  trip_id integer,
  tstamp timestamptz,
  speed float,
  distance float,
  temperature float,
  ,...);
Run Code Online (Sandbox Code Playgroud)

我推荐这门课程的情况并不多,只有当这是一个不值得你花费太多时间的小项目时。

尺寸和事实

因此,如果您已经清除了问题(1)的障碍,并且您想要一个更高的性能模式,那么这是首先要考虑的选项之一。它包括一些基本的规范化,但从测量的“事实”数量中提取“维度”数量。

本质上,您需要一个表格来记录有关旅行的信息,

CREATE trips(
  trip_id integer,
  other_info text);
Run Code Online (Sandbox Code Playgroud)

和一个记录时间戳的表,

CREATE tstamps(
  tstamp_id integer,
  tstamp timestamptz);
Run Code Online (Sandbox Code Playgroud)

最后是所有测量过的事实,以及对维度表的外键引用(即meas_facts(trip_id)参考trips(trip_id)meas_facts(tstamp_id)参考tstamps(tstamp_id)

CREATE meas_facts(
  trip_id integer,
  tstamp_id integer,
  speed float,
  distance float,
  temperature float,
  ,...);
Run Code Online (Sandbox Code Playgroud)

乍一看,这似乎没什么帮助,但是如果您有例如数千个并发行程,那么它们可能都每秒进行一次测量,即秒。在这种情况下,您每次都必须为每次旅行重新记录时间戳,而不仅仅是使用表中的单个条目tstamps

用例:如果您要记录数据的并发行程很多,并且您不介意一起访问所有测量类型,则这种情况会很好。

由于 Postgres 按行读取,任何时候,例如,speed给定时间范围内的测量值,您都必须从meas_facts表中读取整行,这肯定会减慢查询速度,但如果您正在使用的数据集是不要太大,那么你甚至不会注意到差异。

拆分您测量的事实

为了进一步扩展最后一部分,您可以将您的测量值分解成单独的表格,例如,我将显示速度和距离表格:

CREATE speed_facts(
  trip_id integer,
  tstamp_id integer,
  speed float);
Run Code Online (Sandbox Code Playgroud)

CREATE distance_facts(
  trip_id integer,
  tstamp_id integer,
  distance float);
Run Code Online (Sandbox Code Playgroud)

当然,您可以看到这可能如何扩展到其他测量。

用例:因此,这不会使您的查询速度显着提高,当您查询一种测量类型时,速度可能只会线性增加。这是因为当您想要查找有关速度的信息时,您只需要从speed_facts表中读取行,而不是在meas_facts表的一行中出现的所有额外的、不需要的信息。

因此,您只需要阅读有关一种测量类型的大量数据,就可以获得一些好处。对于您建议的以一秒为间隔读取 10 小时数据的情况,您只会读取 36,000 行,因此您永远不会真正发现这样做的重大好处。但是,如果您要查看大约 10 个小时的 5,000 次行程的速度测量数据,现在您正在查看读取 1.8 亿行数据。只要您一次只需要访问一种或两种测量类型,这种查询速度的线性增加可能会产生一些好处。

数组/HStore/ & TOAST

您可能不需要担心这部分,但我知道它确实重要的情况。如果您需要访问的巨大数额的时间序列数据,而你知道你需要访问一个巨大的块中的所有的话,你可以使用一个结构,它会利用的TOAST表,基本上存储在大数据,压缩段。只要您的目标是访问所有数据,就可以更快地访问数据。

一个示例实现可能是

CREATE uber_table(
  trip_id integer,
  tstart timestamptz,
  speed float[],
  distance float[],
  temperature float[],
  ,...);
Run Code Online (Sandbox Code Playgroud)

在这个表中,tstart将存储数组中第一个条目的时间戳,每个后续条目将是下一秒的读数值。这需要您在一个应用软件中管理每个数组值的相关时间戳。

另一种可能是

CREATE uber_table(
  trip_id integer,
  speed hstore,
  distance hstore,
  temperature hstore,
  ,...);
Run Code Online (Sandbox Code Playgroud)

您可以将测量值添加为(键,值)对(时间戳,测量)。

用例:这是一个可能最好留给更熟悉 PostgreSQL 的人的实现,并且仅当您确定您的访问模式需要是批量访问模式时。

结论?

哇,这比我预期的要长得多,抱歉。:)

从本质上讲,有多种选择,但使用第二个或第三个选项可能会获得最大的收益,因为它们适合更一般的情况。

PS:您最初的问题暗示您将在收集完所有数据后批量加载数据。如果您将数据流式传输到您的 PostgreSQL 实例,您将需要做一些进一步的工作来处理您的数据摄取和查询工作负载,但我们将留待下次再做。;)


Pir*_*App 5

它是2019 年,这个问题值得更新。

  • 无论该方法是否最好,我都会让您进行基准测试和测试,但这是一种方法。
  • 使用名为timescaledb的数据库扩展
  • 这是安装在标准 PostgreSQL 上的扩展,可以处理在合理地存储时间序列时遇到的几个问题

以您为例,首先在 PostgreSQL 中创建一个简单的表

第1步

CREATE TABLE IF NOT EXISTS trip (
    ts TIMESTAMPTZ NOT NULL PRIMARY KEY,
    speed REAL NOT NULL,
    distance REAL NOT NULL,
    temperature REAL NOT NULL
) 
Run Code Online (Sandbox Code Playgroud)

第2步

  • 把它变成timescaledb 世界中所谓的超级表
  • 简而言之,它是一个大表,它被连续分成一些时间间隔的小表,比如一天,每个小表被称为一个块
  • 尽管您可以在查询中包含或排除它,但在运行查询时此迷你表并不明显

    SELECT create_hypertable('trip', 'ts', chunk_time_interval => 间隔 '1 小时', if_not_exists => TRUE);

  • 我们上面所做的是将我们的行程表,每小时根据列 'ts' 将其划分为迷你块表。如果您添加 10:00 到 10:59 的时间戳,它们将被添加到 1 个块中,但 11:00 将被插入到一个新块中,并且这将无限持续下去。

  • 如果您不想无限地存储数据,您还可以使用 DROP 超过 3 个月的数据块

    SELECT drop_chunks(interval '3 个月', 'trip');

  • 您还可以使用以下查询获取迄今为止创建的所有块的列表

    SELECT chunk_table, table_bytes, index_bytes, total_bytes FROM chunk_relation_size('trip');

  • 这将为您提供迄今为止创建的所有迷你表的列表,如果需要,您可以在最后一个迷你表上运行查询

  • 您可以优化查询以包含、排除块或仅对最后 N 个块进行操作等