【问题标题】:Choosing the right partitioning rule选择正确的分区规则
【发布时间】:2019-04-24 22:49:26
【问题描述】:

我正在建立一个新的 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 将仅扫描正确的表...?

谢谢,

尼哥

【问题讨论】:

    标签: postgresql database-partitioning


    【解决方案1】:

    限制在于规划器而不是分区本身。手册中有详细介绍:

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

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

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

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

    1. 也许以后再做决定。等到需要分区。
    2. 您不太可能需要正好 10 个分区,包含 2000 行和 20 亿行。

    如果您要进行分区,请按范围进行 - 比如说 100,000 行或每个分区 100 万行。添加一个 cron-job 以检查使用的最大 ID,并在需要时创建一个新分区(可能每天一次)。

    不过,就我个人而言,我会把它留到需要它的时候。如果您认为以后很可能需要它们,也许可以将单个分区作为一个包罗万象的分区。

    【讨论】:

      【解决方案2】:

      WHERE 需要与CHECK 在同一个表达式上,即。例如,查询规划器不会意识到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 在相当大的表上可以非常快,如果不进行分区,它会带你走很远。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-07
        • 2010-12-22
        • 2017-07-23
        • 1970-01-01
        • 2011-11-14
        • 2023-02-21
        • 2019-06-04
        相关资源
        最近更新 更多