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 亿行数据。只要您一次只需要访问一种或两种测量类型,这种查询速度的线性增加可能会产生一些好处。
您可能不需要担心这部分,但我知道它确实重要的情况。如果您需要访问的巨大数额的时间序列数据,而你知道你需要访问一个巨大的块中的所有的话,你可以使用一个结构,它会利用的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 实例,您将需要做一些进一步的工作来处理您的数据摄取和查询工作负载,但我们将留待下次再做。;)
它是2019 年,这个问题值得更新。
以您为例,首先在 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步
尽管您可以在查询中包含或排除它,但在运行查询时此迷你表并不明显
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 个块进行操作等
归档时间: |
|
查看次数: |
12246 次 |
最近记录: |