PostgreSQLのバージョン10では、宣言型テーブルパーティショニングが追加されました。 機能。バージョン11(現在ベータ版)では、これを外部データラッパーと組み合わせることができます。 、複数のPostgreSQLサーバー間でテーブルをネイティブにシャーディングするメカニズムを提供します。
毎日の都市の毎日の最低気温と最高気温を保存するテーブルを考えてみましょう。
CREATE TABLE temperatures (
at date,
city text,
mintemp integer,
maxtemp integer
);
テーブルの仕様には、物事を単純にするために列の制約と主要なキーが意図的に含まれていません。これらは後で追加します。
多くのアプリケーションでは、最新のデータがより頻繁にアクセスされることがよくあります。現在の会計年度、今月、最後の1時間などを考えてみてください。 「温度」テーブルが大きくなるにつれて、古いデータを同じ構造の別のテーブルに移動するのが理にかなっています。たとえば、次のようにすることができます:
CREATE TABLE temperatures_2017 (LIKE temperatures);
INSERT INTO temperatures_2017 SELECT * FROM temperatures WHERE
extract(year from at) = 2017;
DELETE FROM temperatures WHERE extract(year from at) = 2017;
2017年のすべてのエントリを別のテーブルに移動します。これにより、メインの「温度」テーブルが小さくなり、アプリケーションが処理できるようになります。ボーナスとして、古いデータを削除する必要がある場合は、古いデータが存続するため、メイン/現在のテーブルへの受信データの挿入を遅くすることなく削除できます。別のテーブルにあります。
ただし、複数の個別のテーブルがあるということは、アプリケーションコードを変更する必要があることを意味します。都市の年間最低気温と最高気温を取得するなど、古いデータにアクセスする必要がある場合は、スキーマに存在するテーブルを見つけ、それぞれにクエリを実行し、各テーブルの結果を組み合わせる必要があります。アプリケーションコードを変更せずにこれを行うことはできますか?
パーティショニングはこれを可能にします。 PostgreSQL 10では、次のような「温度」テーブルを作成できます。
CREATE TABLE temperatures (
at date,
city text,
mintemp integer,
maxtemp integer
)
PARTITION BY RANGE (at);
これにより、「温度」がパーティションマスターテーブルになり、重複しないデータを格納する複数のパーティションテーブルを作成し、それぞれに異なる「at」値のセットを設定するようにPostgreSQLに通知します。マスターテーブル自体はデータを保持しませんが、アプリケーションからクエリを実行したり、アプリケーションから挿入したりできます。これは、実際のデータを保持している子パーティションを認識しません。
そして、これが私たちのパーティションです:
CREATE TABLE temperatures_2017
PARTITION OF temperatures
FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');
CREATE TABLE temperatures_2018
PARTITION OF temperatures
FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');
現在、2つのテーブルがあります。1つは2017年のデータを格納し、もう1つは2018年のデータを格納します。「from」の値は含まれますが、「to」の値は含まれないことに注意してください。試してみましょう:
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-# VALUES ('2018-08-03', 'London', 63, 90);
INSERT 0 1
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-# VALUES ('2017-08-03', 'London', 59, 70);
INSERT 0 1
temp=# SELECT * FROM temperatures;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2017-08-03 | London | 59 | 70
2018-08-03 | London | 63 | 90
(2 rows)
temp=# SELECT * FROM temperatures_2017;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2017-08-03 | London | 59 | 70
(1 row)
temp=# SELECT * FROM temperatures_2018;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2018-08-03 | London | 63 | 90
(1 row)
「アプリケーション」はメインテーブルに挿入して選択できますが、PostgreSQLは実際のデータを適切な子テーブルにルーティングします。 (ああ、BTW、それらの温度は本物です!)
インデックスとテーブルおよび列の制約は、実際のデータが存在する場所であるため、実際にはパーティションテーブルレベルで定義されます。パーティションテーブルの作成中にこれらを設定できます:
CREATE TABLE temperatures_2017
PARTITION OF temperatures (
mintemp NOT NULL,
maxtemp NOT NULL,
CHECK (mintemp <= maxtemp),
PRIMARY KEY (at, city)
)
FOR VALUES FROM ('2017-01-01') TO ('2018-01-01');
PostgreSQL 11では、親テーブルにインデックスを定義でき、既存および将来のパーティションテーブルにインデックスを作成します。詳しくはこちらをご覧ください。
外部データラッパー機能は、しばらくの間Postgresに存在していました。 PostgreSQLでは、このメカニズムを使用して、他のサーバーやシステムに保存されているデータにアクセスできます。私たちが興味を持っているのは「postgres_fdw」です。これにより、あるPostgresサーバーから別のサーバーにアクセスできるようになります。
「postgres_fdw」は標準ディストリビューションに存在する拡張機能であり、通常のCREATEEXTENSIONコマンドでインストールできます。
CREATE EXTENSION postgres_fdw;
「box2db」というデータベースを持つ別のPostgreSQLサーバー「box2」があると仮定します。このための「外部サーバー」を作成できます:
CREATE SERVER box2 FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'box2', dbname 'box2db');
また、ユーザー「alice」(ログインしているユーザー)をbox2ユーザー「box2alice」にマッピングしましょう。これにより、リモートテーブルにアクセスするときに「alice」を「box2alice」にすることができます。
CREATE USER MAPPING FOR alice SERVER box2
OPTIONS (user 'box2alice');
これで、box2のテーブル(ビュー、マットビューなど)にアクセスできます。まず、box2にテーブルを作成し、次にサーバーに「外部テーブル」を作成します。外部テーブルは実際のデータを保持しませんが、テーブルボックス2にアクセスするためのプロキシとして機能します。
-- on box2
CREATE TABLE foo (a int);
-- on your server
IMPORT FOREIGN SCHEMA public LIMIT TO (foo)
FROM SERVER box2 INTO public;
サーバー内の外部テーブルは、通常のテーブルと同じ方法でトランザクションに参加できます。アプリケーションは、相互作用するテーブルがローカルまたは外部であることを知る必要はありません。ただし、アプリケーションが外部テーブルから多くの行をプルするSELECTを実行すると、処理速度が低下する可能性があります。Postgres10では、結合をプッシュダウンするための改善が行われました。リモートサーバーに集約します。
そして今、楽しい部分です。リモートサーバーにパーティションを設定します。
まず、box2に物理パーティションテーブルを作成しましょう:
-- on box2
CREATE TABLE temperatures_2016 (
at date,
city text,
mintemp integer,
maxtemp integer
);
次に、外部テーブルとしてサーバー上にパーティションを作成します。
CREATE FOREIGN TABLE temperatures_2016
PARTITION OF temperatures
FOR VALUES FROM ('2016-01-01') TO ('2017-01-01')
SERVER box2;
これで、独自のサーバーから挿入してクエリを実行できます:
temp=# INSERT INTO temperatures (at, city, mintemp, maxtemp)
temp-# VALUES ('2016-08-03', 'London', 63, 73);
INSERT 0 1
temp=# SELECT * FROM temperatures ORDER BY at;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2016-08-03 | London | 63 | 73
2017-08-03 | London | 59 | 70
2018-08-03 | London | 63 | 90
(3 rows)
temp=# SELECT * FROM temperatures_2016;
at | city | mintemp | maxtemp
------------+--------+---------+---------
2016-08-03 | London | 63 | 73
(1 row)
あります!バージョン11では、リモートパーティションに行を挿入できるようになりました。この機能により、データを論理的(パーティション)および物理的(FDW)にシャーディングできるようになりました。
VACUUMやANALYZEなどのコマンドは、パーティションマスターテーブルで期待どおりに機能します。すべてのローカル子テーブルは、VACUUMとANALYZEの対象になります。パーティションを切り離して、データをパーティションの制約なしに操作してから、再接続することができます。子テーブル自体をパーティション化できます。
データの移動(「再シャーディング」)は、通常のSQLステートメント(挿入、削除、コピーなど)を使用して実行できます。パーティションローカルインデックスとトリガーを作成できます。
シャードに冗長性を追加することは、論理またはストリーミングレプリケーションで簡単に実現できます。