Postgres:大型连接优化

时间:2015-05-07 23:34:06

标签: postgresql

我有两张桌子,让我们说

NoReverseMatch: Reverse for 'websitedetail' with arguments '()' and keyword arguments '{'pk': 42, 'slug': u'when-you-talk-you-hardly-even-look-in-my-eyes'}' not found. 1 pattern(s) tried: ['dashboard/(?P<slug>[-\\w]+])-by-(?P<pk>\\d+)/$']

我是以下列方式加入他们的:

CREATE TABLE a (
  a_a BIGINT,
  a_b BIGINT,
  a_c BIGINT,
  a_someval NUMERIC
);

CREATE TABLE b (
  b_a BIGINT,
  b_b BIGINT,
  b_c BIGINT,
  b_someval NUMERIC
);

解释显示,该计划程序需要对JOIN中使用的列进行排序。

有没有办法对这些表进行预先排序,以便每次加入时都不会对它们进行排序?

一些可能很重要的事情:

  • 查询使用两个表的整个内容(不是一小部分行)
  • 每个表中有数亿行
  • 表的内容不会改变 - 在用于分析需求的生产数据库的快照中生成这两个表(CREATE TABLE x AS SELECT ...)

2 个答案:

答案 0 :(得分:0)

如果您确实希望确保表格基本上在join之前编辑和排序,则可以创建joinjoin个表格。

这将导致物化视图已将两个表的结果CREATE MATERIALIZED VIEW ab_mat AS SELECT * FROM a JOIN b ON (a.a_a = b.b_a AND a.a_b = b.b_b AND a.a_c = b.b_c); 编辑在一起并按您选择的顺序排序。您还可以在任何字段上创建索引,这与常规视图不同。

代码将类似于:

REFRESH MATERIALIZED VIEW

这种方法的一个潜在缺点是物化视图无法更新,因此信息不是实时的(这就是为什么它们提供了更好的性能 - 它们本质上是持久化的视图快照到磁盘)。但是,对于许多用例来说,这是完全正常的。

要更新信息,只需创建一个 cron作业,在您需要的时间间隔内定期在物化视图上运行SELECT * FROM ab_mat -- optional ordering order by a, b, c; 命令。这可以从相对激进(例如每5分钟)到相对宽松(例如每5分钟),例如相对宽松。每天或每周。

请注意,物化视图可以依次连接到其他表和视图,以混合实时信息。我最近使用了这样的混合设置来极大地提高极其复杂的查询的速度,其中只有一些数据需要真正实时。

另请注意,物化视图在9.3之前的版本中不可用。

根据OP的评论进行编辑:

您可以选择在视图中指定顺序,在这种情况下,它将默认为该顺序,或者您可以将其保持无序,如上所述,并且每次都动态订购。

您可以像这样查询物化视图

join

这意味着它根本不需要做任何return randomNumber; ,因为它已经完成并保存了。

答案 1 :(得分:0)

我很惊讶这需要排序,但如果是这样,那么诀窍是获得一个大的工作内存区域。我本来希望哈希加入是诚实的。

您可以考虑是否可以实现两个表的分区,两个源表在同一个键定义上进行分区。我不确定PostgreSQL是否与Oracle类似地实现了分区智能连接,但如果没有,那么您可以使用以下查询手动实现它:

SELECT *
FROM a_part01
JOIN b_part01 ON (a.a_a = b.b_a AND a.a_b = b.b_b AND a.a_c = b.b_c)
union all
SELECT *
FROM a_part02
JOIN b_part02 ON (a.a_a = b.b_a AND a.a_b = b.b_b AND a.a_c = b.b_c)
union all
...
union all
SELECT *
FROM a_part0n
JOIN b_part0n ON (a.a_a = b.b_a AND a.a_b = b.b_b AND a.a_c = b.b_c);

...或作为一系列单独的查询:

CREATE TABLE result
AS
SELECT *
FROM a_part01
JOIN b_part01 ON (a.a_a = b.b_a AND a.a_b = b.b_b AND a.a_c = b.b_c);

...

INSERT INTO result
SELECT *
FROM a_part0n
JOIN b_part0n ON (a.a_a = b.b_a AND a.a_b = b.b_b AND a.a_c = b.b_c)

这允许以更低的内存占用完成查询。

关于预先分配表数据,我不确定PostgreSQL是否在插件或表创建时尊重ORDER BY,但您可以轻松地测试它以查找。如果是这样,您可以对表进行排序,但数据库不会知道它们已排序。但是,实际意义可能只是连接更有效,因为对已经排序的数据进行排序可能更有效。我会说它绝对值得测试。

但是,您仍然在实施一种数据,只是在整个操作的不同部分。

如果索引覆盖了表的所有列,那么实际使用索引可能会有所帮助。虽然创建索引需要排序,所以你只是在其他地方做工作。