適切なパーティション化ルールの選択

私は何百万(または多分数十億)の行を含む新しい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は正しいテーブルのみをスキャンします...?

ありがとう、 ニコ

5

2 答え

この制限は、パーティショニング自体ではなく、プランナーによるものです。このマニュアルでは、次のように詳しく説明しています。

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

考慮すべき点は2つありますが、

まず、すべてのアクセスが主キーを介して行われるとします。つまり、少なくとも通常の使用ではなく、パーティション化によるパフォーマンス上の利点は得られません。各パーティションのインデックスは小さくなりますが、PGは最初に確認するパーティションを選択する必要があります。あなたが再索引やそれに類することが必要な場合は、各パーティションを別々に再インデックスすることができます。

第二に、あなたは数千から数十億行の何かを持っているかもしれないと言います。これは2つの結論につながります。

  1. おそらく後でその決定を残します。パーティションを作成するまで待ってください。
  2. 2行、20億のパーティションが10個必要なことはほとんどありません。

パーティション分割を行う場合は、範囲ごとに実行します。たとえば、パーティションごとに100,000行または100万行を指定します。 cron-jobを追加して、使用される最大IDを確認し、必要に応じて(おそらく1日に1回)新しいパーティションを作成します。

個人的には、私が必要になるまで残しておきます。おそらく後で必要になる可能性が高いと思われる場合は、単一のパーティションをキャッチオールとして使用することもできます。

5
追加された

WHERE CHECK と同じ式にする必要があります。つまり、 user_id = 12345 user_id%10 = 5 という結論を出すことをクエリプランナーは認識しません。試す

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

つまり、私は第2の Richard Huxtonの答えを参考にしたいと思います。早すぎる最適化を避けるために、データセットのサイズ。 Postgresはかなり大きなテーブルでは非常に高速になりますが、分割することなくかなり離れています。

1
追加された