选择正确的分区规则

时间:2011-11-25 10:00:45

标签: postgresql database-partitioning

我正在建立一个新的PostgreSQL 9数据库,其中包含数百万(或数十亿)行。所以我决定使用PostgreSQL继承来分区数据。

我创建了一个这样的主表(例如简化):

CREATE TABLE mytable
(
  user_id integer,
  year integer,
  CONSTRAINT pk_mytable PRIMARY KEY (user_id, year)
);

10个分区表:

CREATE TABLE mytable_0 () INHERITS (mytable);
CREATE TABLE mytable_1 () INHERITS (mytable);
...
CREATE TABLE mytable_9 () INHERITS (mytable);

我知道将始终使用唯一的user_id条件从应用程序访问行。 所以我想使用基于user_id的规则在10个表中“相当”地传播数据。

要调整主表上的查询,我的第一个想法是使用模数检查约束:

ALTER TABLE mytable_0 ADD CONSTRAINT mytable_user_id_check CHECK (user_id % 10 = 0);
ALTER TABLE mytable_1 ADD CONSTRAINT mytable_user_id_check CHECK (user_id % 10 = 1);
...

问题是,当我使用user_id上的条件查询主表“mytable”时,PostgreSQL分析器会检查所有表,并且不会受益于检查约束:

EXPLAIN SELECT * FROM mytable WHERE user_id = 12345;

"Result  (cost=0.00..152.69 rows=64 width=36)"
"  ->  Append  (cost=0.00..152.69 rows=64 width=36)"
"        ->  Seq Scan on mytable  (cost=0.00..25.38 rows=6 width=36)"
"              Filter: (user_id = 12345)"
"        ->  Seq Scan on mytable_0 mytable  (cost=0.00..1.29 rows=1 width=36)"
"              Filter: (user_id = 12345)"
"        ->  Seq Scan on mytable_1 mytable  (cost=0.00..1.52 rows=1 width=36)"
"              Filter: (user_id = 12345)"
...
"        ->  Seq Scan on mytable_9 mytable  (cost=0.00..1.52 rows=1 width=36)"
"              Filter: (user_id = 12345)"

然而,如果我使用这样的经典CHECK CONSTRAINT(以及与该规则匹配的重新分区):

ALTER TABLE mytable_0 ADD CONSTRAINT mytable_user_id_check CHECK (user_id BETWEEN 1 AND 10000);
ALTER TABLE mytable_1 ADD CONSTRAINT mytable_user_id_check CHECK (user_id BETWEEN 10001 AND 20000);
...

它只会扫描符合条件的表格(本例中为mytable和mytable_1):

"Result  (cost=0.00..152.69 rows=64 width=36)"
"  ->  Append  (cost=0.00..152.69 rows=64 width=36)"
"        ->  Seq Scan on mytable  (cost=0.00..25.38 rows=6 width=36)"
"              Filter: (user_id = 12345)"
"        ->  Seq Scan on mytable_1 mytable  (cost=0.00..1.52 rows=1 width=36)"
"              Filter: (user_id = 12345)"

但使用此类检查约束很难维护,因为将在表中填充的用户范围将在多年内发生变化。在不久的将来成千上万,甚至数百万或更多...

我可以使用什么规则在10个表中平均分配我的数据,这些数据可以受益于检查约束,以便主表上的SELECT只扫描正确的表...?

谢谢,

尼科

2 个答案:

答案 0 :(得分:5)

限制是与计划者而不是分配本身。手册中详细介绍了这一点:

http://www.postgresql.org/docs/9.1/static/ddl-partitioning.html

你提到的有两件事需要考虑。

首先,您说所有访问都将通过主键进行。这意味着您将不会从分区中获得任何性能优势(至少在正常使用情况下不会)。每个分区的索引都会更小,但PG需要先选择要检查的分区。如果您需要重新索引或类似的话,您将获得的收益 - 您可以单独重新索引每个分区。

其次,你说你可能有数千到数十亿行。这让我得出两个结论:

  1. 也许稍后决定。等到你需要分区。
  2. 你不太可能只想要10个分区,分别有两千行和二十亿个。
  3. 如果要进行分区,请按范围进行分区 - 比如每行分区100,000行或100万行。添加一个cron-job来检查所使用的最大ID,并根据需要创建一个新分区(也许每天一次)。

    但就个人而言,我会离开它直到我需要它。如果您认为它更可能在以后需要它们,也许有一个单独的分区作为一个全能。

答案 1 :(得分:1)

WHERE需要与CHECK相同的表达式,i。例如,查询计划程序将不会意识到user_id = 12345允许得出user_id % 10 = 5的结论。尝试

EXPLAIN SELECT * FROM mytable WHERE user_id = 12345 AND user_id % 10 = 5;

那就是说,我想第二个Richard Huxton's answer,你可能想要推迟分区,直到你有更多关于数据集大小的信息,这是为了避免过早优化。 Postgres在相当大的表格上可以非常快,如果没有分区,它会占用很长的时间。

相关问题