管理和加速对超过 3 万亿行的 PostgreSQL 表的查询

Gre*_*reg 7 postgresql index database-design database-recommendation partitioning

我有超过 10 年的时间序列数据,有超过 3 万亿行和 10 列。

目前我使用具有 128GB RAM 的 PCIe SSD,我发现查询需要大量时间。例如,运行以下命令需要超过 15 分钟:

SELECT * FROM tbl WHERE column_a = 'value1' AND column_b = 'value2';
Run Code Online (Sandbox Code Playgroud)

该表主要用于读取。写入表的唯一时间是在每周更新期间插入大约 1500 万行。

管理如此大的表的最佳方法是什么?您会建议按年份拆分吗?

表大小为 542 GB,外部大小为 109 GB。

EXPLAIN (BUFFERS, ANALYZE) 输出:

"Seq Scan on table  (cost=0.00..116820941.44 rows=758 width=92) (actual time=0.011..1100643.844 rows=667 loops=1)"
"  Filter: (("COLUMN_A" = 'Value1'::text) AND ("COLUMN_B" = 'Value2'::text))"
"  Rows Removed by Filter: 4121893840"
"  Buffers: shared hit=2 read=56640470 dirtied=476248 written=476216"
"Total runtime: 1100643.967 ms"
Run Code Online (Sandbox Code Playgroud)

该表是使用以下代码创建的:

CREATE TABLE tbl (
  DATE     timestamp with time zone,
  COLUMN_A text,
  COLUMN_B text,
  VALUE_1  double precision,
  VALUE_2  double precision,
  VALUE_3  double precision,
  VALUE_4  double precision,
  VALUE_5  double precision,
  VALUE_6  double precision,
  VALUE_7  double precision,
);

CREATE INDEX ix_table_name_date ON table_name (DATE);
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 6

您现有的索引DATE显然对查询无用。查询的第一个明显步骤:

SELECT * FROM tbl WHERE column_a = 'value1' AND column_b = 'value2';
Run Code Online (Sandbox Code Playgroud)

索引column_acolumn_b(其中曾经是更具选择性的),或者可能在多列索引(column_a, column_b),如:

CREATE INDEX tbl_a_b_idx ON tbl(column_a, column_b);
Run Code Online (Sandbox Code Playgroud)

细节:

接下来,如果您可以安全地从查询中排除表的大部分内容,我将考虑部分索引。或者将您的表划分为 100 个分区。并使用约束排除

正如您所设想的那样,“按年份拆分”将DATE与给定查询的索引一样无用(甚至有害)。分区需要基于查询谓词 (column_acolumn_b) 中的列才能有所帮助。在这方面,仅列上的谓词会容易得多。如果您对各个列有重要的查询过滤,分区可能不是要走的路。(部分索引仍然可能。)

如果分区不好,那么侵入性较小的措施是CLUSTER基于新索引的数据(您不能为此使用部分索引)。或者简单地从查询的排序输出创建一个新表。这特别有趣,因为您的表大多是只读的。应该支付至少这样做一次,但是这一次将是非常昂贵的:整个表必须重新编写一个或其他方式。您需要足够的可用空间、尽可能多的 RAM 以及对表的独占锁。或者使用pg_repack, 避免排他锁:

确保运行最新版本的 Postgres。即将发布的Postgres 9.5对您来说可能特别有趣,因为它引入了BRIN 索引(块范围索引),它可以显着减少非常大的表的索引大小。可能正是您正在寻找的。