Amazon Web Services ブログ

PostgreSQL 11 の新機能を詳しくご紹介

初期のPostgreSQL プロジェクトは 1986 年に大学のプロジェクトとしてスタートしました。1996 年に PostgreSQL プロジェクトはオープンソースコミュニティが引き継ぎ、毎年メジャーバージョンを定期的にリリースしています。ソフトウェアの複雑さを考えると、このような早急なリリーススケジュールには、主要な機能を小さく基本的な要素に分割する必要があります。こうした小規模で基本的な機能を組み合わせることで、最新リリースの PostgreSQL 11 を含む PostgreSQL のすべてのメジャーリリースが行われています。PostgreSQL 11 は、Amazon RDS for PostgreSQL および PostgreSQL と互換性のある Amazon Aurora の両方に対応しています。

この記事では、パーティション分割、並列処理、ジャストインタイム (JIT) コンパイルという、PostgreSQL 11 の 3 つの素晴らしい機能について詳しく説明します。複数の PostgreSQL バージョンにおけるこれらの機能の進化について調べます。また、PostgreSQL 11 が提供する利点についても説明し、これらの機能をアプリケーションに適合させる方法を説明する実用的な例を示します。

パーティション分割

データベースが成長するにつれ、少数のテーブルが通常は成長の主な原因となっています。成長は、すべてのアクティビティの履歴ログを保持するテーブルや、ユーザーテーブルに集中する場合があります。

テーブルのパーティション分割により、データベースの大幅な成長に対処できます。つまり、単一の大きなテーブルをより小さく管理しやすいチャンク (パーティション) に分割することで、大きなテーブルでのクエリを高速化します。クエリの実行中にデータベースがパーティション全体を除外できると、処理するデータが大幅に減るため、パフォーマンスが向上します。テーブルを分割するという概念は PostgreSQL 11 の新機能ではありません。PostgreSQL では、2005 年リリースのバージョン 8.1 において最初にテーブルのパーティション分割という形式を導入しました。

以下の orders テーブルを例に考えてみましょう。アプリケーションは、販売注文がある度に新しい行をこのテーブルに追加します。注文が多くなるにつれ、このテーブルは日ごとに大きくなります。このように時間の流れと共に成長するテーブルには、時間に基づくパーティション分割が基本的に使われます。

CREATE TABLE orders (
          o_orderkey INTEGER,
          o_custkey INTEGER,
          o_orderstatus CHAR(1),
          o_totalprice REAL,
          o_orderdate DATE,
          o_orderpriority CHAR(15),
          o_clerk CHAR(15),
          o_shippriority INTEGER,
          o_comment VARCHAR(79));

PostgreSQL のバージョン 8.1 から 9.6 では、「テーブル継承」という固有の機能を使用してパーティション分割を設定します。 つまり、テーブルの制約を持つ親から継承した子テーブルを作成し、その子テーブルに含まれるデータ範囲を強制することにより、パーティションを毎年設定します。

その後 PostgreSQL 最適化ツールは、排他制約と呼ばれる機能を使用します。この機能は制約を使用してクエリ時にパーティションを除外し、クエリのパフォーマンスを向上させます。

CREATE TABLE orders_2015 (
CHECK ( o_orderdate >= DATE '2015-01-01'
AND o_orderdate < DATE '2016-01-01')
) INHERITS (orders);

CREATE TABLE orders_2016 (
CHECK ( o_orderdate >= DATE '2016-01-01'
AND o_orderdate < DATE '2017-01-01')
) INHERITS (orders);

排他制約は効果的ですが、この方法は子継承モデルにとって欠点があります。具体的に言うと、排他制約により、パーティション分割されたテーブルへのデータの読み込みが複雑になります。そのため、適切な子に行を移動するには、親テーブルでトリガーする必要があります。

CREATE OR REPLACE FUNCTION partition_trigger()
  RETURNS TRIGGER AS $$
BEGIN
  EXECUTE 'INSERT INTO orders_' ||
          to_char(NEW.o_orderdate, 'YYYY') ||
          ' VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)'
  USING NEW.o_orderkey, NEW.o_custkey, NEW.o_orderstatus, NEW.o_totalprice,
        NEW.o_orderdate, NEW.o_orderpriority, NEW.o_clerk, NEW.o_shippriority,
        NEW.o_comment;

  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

PostgreSQL 10 では、宣言的なパーティション分割の構文を提供しています。パーティション分割メソッドとパーティション分割キーを定義します。PostgreSQL 10 は、範囲パーティション分割メソッドおよびリストパーティション分割メソッドをサポートします。

分かりやすいパーティション分割の構文に加えて、PostgreSQL 10 では親テーブルのトリガーをする必要がなくなりました。トリガーをしなければ、コードがなくてもテーブルをパーティション分割でき、パーティション分割の構文によりテーブルのメンテナンスが容易になります。PostgreSQL は、行を正しいパーティションに自動的にルーティングするようになったので、データのロードとメンテナンスの両方が改善されました。

名前から分かるように、範囲パーティション分割が扱うのは値の範囲です。たとえば時系列データでは、日付範囲を使用して、毎日、毎月、または毎年のパーティションを作成できます。これで orders テーブルに完璧に合うようになりました。

CREATE TABLE orders (
        o_orderkey INTEGER,
        o_custkey INTEGER,
        o_orderstatus CHAR(1),
        o_totalprice REAL,
        o_orderdate DATE,
        o_orderpriority CHAR(15),
        o_clerk CHAR(15),
        o_shippriority INTEGER,
        o_comment VARCHAR(79))
PARTITION BY RANGE (o_orderdate);

CREATE TABLE orders_2015 
  PARTITION OF orders 
  FOR VALUES FROM ('2015-01-01') 
               TO ('2016-01-01');

CREATE TABLE orders_2016 
  PARTITION OF orders 
  FOR VALUES FROM ('2016-01-01') 
               TO ('2017-01-01');

固定値の数が限られている場合は、リストパーティション分割を使用します。パーティション作成中に、各パーティションの値のリストを定義します。ここでは、customer テーブルにリストパーティション分割を使用し、各リージョンに属する国のリストを使ってリージョンごとにパーティション分割します。

CREATE TABLE customers (
        c_custkey INTEGER,
        c_name VARCHAR(25),
        c_address VARCHAR(40),
        c_nationkey INTEGER,
        c_phone CHAR(15),
        c_acctbal REAL,
        c_mktsegment CHAR(10),
        c_comment VARCHAR(117))
PARTITION BY LIST (c_nationkey);

CREATE TABLE customers_asia 
  PARTITION OF customers 
  FOR VALUES IN (8, 9, 12, 18, 21);

CREATE TABLE customers_europe 
  PARTITION OF customers
  FOR VALUES IN (6, 7, 19, 22, 23);

パーティション分割メソッドとパーティション分割キーのうちどちらを選択するのが正しいかは、アプリケーションによって異なります。データの変更に応じて、選択を変更することもできます。範囲パーティション分割とリストパーティション分割が、同じスキーマで必要になる場合があります。

PostgreSQL 11 ではこの構文をさらに拡張し、デフォルトのパーティションを定義できるようにしています。デフォルトのパーティションは、確立されたパーティションの範囲外の値を持つデータを取得する場合に役立ちます。たとえば、ここでは orders テーブルに 2015 年以降の毎年のパーティションがあります。ただし、注文日が空欄になっている行 (たとえば 1970 年 1 月 1 日) を追加できます。エラーは発生しませんが、確立されたパーティションの範囲外の値を持つ行がこの行にデフォルト設定されます。

CREATE TABLE orders_default
  PARTITION OF orders DEFAULT;

PostgreSQL 11 ではパーティションテーブルでのインデックス作成も簡単です。PostgreSQL 10 では、すべてのパーティションでローカルインデックスを作成するには、各パーティションで個別に作成するしかありません。メインの親テーブルにインデックスを作成しようとすると、エラーが返されます。

たとえば、以下のステートメントでは PostgreSQL 10 の orders テーブルにインデックスを作成しようとしています。この試行には予想どおりエラーが返されます。そこで、パーティションにインデックスを作成してみます。

=> CREATE INDEX orders_o_orderdate_o_orderkey_idx
-> ON orders (o_orderdate, o_orderkey);
ERROR:  cannot create index on partitioned 
        table “orders“

=> CREATE INDEX orders_2018_o_orderdate_o_orderkey_idx 
->     ON orders_2018 (o_orderdate, o_orderkey);
CREATE INDEX 

対照的に、PostgreSQL 11 の orders テーブルにインデックスを作成しようとすると、ステートメントはすべてのパーティションにローカルインデックスを構築することができました。

=> CREATE INDEX orders_o_orderdate_o_orderkey_idx
-> ON orders (o_orderdate, o_orderkey);
CREATE INDEX

PostgreSQL 11 では、PostgreSQL 10 で導入された範囲メソッドおよびリストメソッドに追加するハッシュパーティション分割メソッドも導入しています。ハッシュパーティション分割は、パーティションをする必要のある論理値または自然値の範囲を含まない大きなテーブルの場合に便利です。

このセールスデータベースでは、part テーブルにはハッシュパーティション分割が完全に適しています。テーブルは時間の経過と共に成長する設計になっていますが、時間の値がテーブルの行を決定することはありません。新しいデータと同じ頻度で古いデータを参照できます。このテーブルは、テーブルのプライマリキーをハッシュパーティション分割するのに最適なユースケースを表しています。

CREATE TABLE part (
        p_partkey INTEGER,
        p_name VARCHAR(55),
        p_mfgr CHAR(25),
        p_brand CHAR(10),
        p_type VARCHAR(25),
        p_size INTEGER,
        p_container CHAR(10),
        p_retailprice REAL,
        p_comment VARCHAR(23))
PARTITION BY HASH (p_partkey);

ここでは作成するパーティション数を決定するだけで済みます。最適なパーティション数はアプリケーションによって異なりますが、たとえば 10 個のパーティションが必要な場合は 10 個のモジュールでパーティションを定義します。

CREATE TABLE part_p0 
  PARTITION OF part 
  FOR VALUES WITH (MODULUS 10, REMAINDER 0);
  
CREATE TABLE part_p1 
  PARTITION OF part 
  FOR VALUES WITH (MODULUS 10, REMAINDER 1);

PostgreSQL 11 ではパーティションの刈り込みがさらに強化されています。つまり、クエリのためにパーティション全体をスキャンしなくてもよいのです。PostgreSQL 10 以前のバージョンでは、クエリ実行計画中にパーティションが除外されます。値の範囲を除外するのに十分な情報が提供された場合、PostgreSQL はスキャンから対応するパーティションを取り除きます。必要な情報は通常、定数を含む WHERE 条件です。このメソッドは簡単なクエリに効果的です。

もちろん、別の小さなテーブルに結合することで大きなパーティションテーブルにアクセスするのが一般的です。しかし、PostgreSQL 11 では実行時にパーティションを除外します。join の結果、不要なパーティションのスキャンが削除されます。以下のクエリは、パーティションの刈り込みの両タイプの例です。

=> EXPLAIN (ANALYZE, VERBOSE)
SELECT lineitem.l_quantity, part.p_name
  FROM lineitem
 INNER JOIN part ON (lineitem.l_partkey = part.p_partkey)
 WHERE lineitem.l_orderkey = 215528935
 ORDER BY lineitem.l_linenumber;

クエリは、両方ともハッシュパーティション分割された 2 つのテーブルを結合します。クエリの WHERE 条件 "WHERE lineitem.l_orderkey = 215528935" を使用すると、PostgreSQL はクエリ計画中に単一のパーティション以外のすべてを除外できます。

これは以下の PostgreSQL のクエリ計画内にある「Index Scan using lineitem_p78_pkey on public.lineitem_p78」で始まる行で確認できます。 クエリ計画では、行の項目のうち、他の 99 個のパーティションは考慮されません。

QUERY PLAN
-------------------------------------------------------------------------------------------------
 Sort  (cost=1278.06..1278.10 rows=15 width=41) (actual time=0.074..0.074 rows=6 loops=1)
   Output: lineitem_p78.l_quantity, part_p0.p_name, lineitem_p78.l_linenumber
   Sort Key: lineitem_p78.l_linenumber
   Sort Method: quicksort  Memory: 25kB
   ->  Nested Loop (cost=0.86..1277.77 rows=15 width=41)(actual time=0.024..0.068 rows=6 loops=1)
         Output: lineitem_p78.l_quantity, part_p0.p_name, lineitem_p78.l_linenumber
         ->  Append  (cost=0.43..8.77 rows=15 width=12) (actual time=0.014..0.016 rows=6 loops=1)
               ->  Index Scan using lineitem_p78_l_orderkey_idx on public.lineitem_p78  
                     (cost=0.43..8.70 rows=15 width=12) (actual time=0.013..0.015 rows=6 loops=1)
                     Output: lineitem_p78.l_quantity, lineitem_p78.l_linenumber,
                             lineitem_p78.l_partkey
                     Index Cond: (lineitem_p78.l_orderkey = 215528935)
         ->  Append (cost=0.43..84.50 rows=10 width=37) (actual time=0.007..0.008 rows=1 loops=6)
               ->  Index Scan using part_p0_pkey on public.part_p0  
                     (cost=0.43..8.45 rows=1 width=37) (never executed)
                     Output: part_p0.p_name, part_p0.p_partkey
                     Index Cond: (part_p0.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p1_pkey on public.part_p1  
                     (cost=0.43..8.45 rows=1 width=37) (actual time=0.006..0.006 rows=1 loops=2)
                     Output: part_p1.p_name, part_p1.p_partkey
                     Index Cond: (part_p1.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p2_pkey on public.part_p2  
                     (cost=0.43..8.45 rows=1 width=37) (never executed)
                     Output: part_p2.p_name, part_p2.p_partkey
                     Index Cond: (part_p2.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p3_pkey on public.part_p3  
                     (cost=0.43..8.45 rows=1 width=37) (never executed)
                     Output: part_p3.p_name, part_p3.p_partkey
                     Index Cond: (part_p3.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p4_pkey on public.part_p4  
                     (cost=0.43..8.45 rows=1 width=37) (never executed)
                     Output: part_p4.p_name, part_p4.p_partkey
                     Index Cond: (part_p4.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p5_pkey on public.part_p5  
                     (cost=0.43..8.45 rows=1 width=37) (actual time=0.007..0.007 rows=1 loops=1)
                     Output: part_p5.p_name, part_p5.p_partkey
                     Index Cond: (part_p5.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p6_pkey on public.part_p6  
                     (cost=0.43..8.45 rows=1 width=37) (actual time=0.007..0.007 rows=1 loops=1)
                     Output: part_p6.p_name, part_p6.p_partkey
                     Index Cond: (part_p6.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p7_pkey on public.part_p7  
                     (cost=0.43..8.45 rows=1 width=37) (actual time=0.006..0.007 rows=1 loops=1)
                     Output: part_p7.p_name, part_p7.p_partkey
                     Index Cond: (part_p7.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p8_pkey on public.part_p8  
                     (cost=0.43..8.45 rows=1 width=37) (never executed)
                     Output: part_p8.p_name, part_p8.p_partkey
                     Index Cond: (part_p8.p_partkey = lineitem_p78.l_partkey)
               ->  Index Scan using part_p9_pkey on public.part_p9  
                     (cost=0.43..8.45 rows=1 width=37) (actual time=0.006..0.007 rows=1 loops=1)
                     Output: part_p9.p_name, part_p9.p_partkey
                     Index Cond: (part_p9.p_partkey = lineitem_p78.l_partkey)

「part」テーブルのスキャンは、これとは異なる動作をします。複数の行が「Index Scan using part_pN_pkey」というテキストで始まります。 クエリ計画中は、partkey テーブルの値は不明です。なので PostgreSQL は「part」テーブルのパーティションをすべてスキャンするか検討する必要があります。PostgreSQL が lineitem テーブルのスキャンを終えると、PostgreSQL では partkey の値が分かります。PostgreSQL は次に、実行時に不要なパーティションを除外できます。

クエリ計画は、「(never executed)」というテキストを含む「Index Scan」の行にこれを反映します。 PostgreSQL は、lineitem から返された行に基づき、10 個のパーティションのうちの 5 個を除外できます。

並列処理

PostgreSQL の旧バージョンでは、各クエリの実行全体にわたって単一の CPU コアを使用しています。PostgreSQL では、並列処理が初めて導入されたバージョン 9.6 以降から、クエリごとに 1 つの CPU に依存していた部分を変更しました。

並列処理により、PostgreSQL は複数の CPU を使用してクエリを実行することができます。これを実現するために、バックグラウンドワーカーはクエリ実行の負荷を共有して、中間結果をリーダープロセスに返します。リーダープロセスはすべてを結合してクライアントに送信します。結果的に PostgreSQL はクエリを迅速に実行できるようになりました。

たとえば、多くのビジネスで標準的に用いられている簡単なクエリである、月別の平均売上高について見てみましょう。以下のクエリでは、前に定義した orders テーブルに対して、2018 年第 1 四半期の月別の平均販売価格を計算しています。

=> SELECT date_part('month', o_orderdate),
          to_char(o_orderdate, 'Month') as month,
          avg(o_totalprice::numeric)::numeric(10,2)
     FROM orders
    WHERE o_orderdate BETWEEN '2018-01-01' AND '2018-03-31'
    GROUP BY 1, 2
    ORDER BY 1;

 date_part |   month   |    avg
-----------+-----------+-----------
         1 | January   | 151136.72
         2 | February  | 151005.58
         3 | March     | 151134.33
(3 rows)

Time: 13143.755 ms (00:13.144)

PostgreSQL は、orders テーブルの 2018 年度のパーティション全体を順次スキャンしてから、数百万の値の平均値を計算します。そのため、結果が返されるまでに 13 秒以上かかります。

PostgreSQL 9.6 には並列シーケンシャルスキャンと並列集計が追加され、複数の CPU を使用して月別の平均売上高のクエリを実行できます。並列処理レベルを 2 に上げると、結果を 2 倍以上速く得ることができます。

=> SET max_parallel_workers_per_gather = 2;
SET

=> SELECT date_part('month', o_orderdate),
          to_char(o_orderdate, 'Month') as month,
          avg(o_totalprice::numeric)::numeric(10,2)
     FROM orders
    WHERE o_orderdate BETWEEN '2018-01-01' AND '2018-03-31'
    GROUP BY 1, 2
    ORDER BY 1;

 date_part |   month   |    avg
-----------+-----------+-----------
         1 | January   | 151136.72
         2 | February  | 151005.58
         3 | March     | 151134.33
(3 rows)

Time: 5113.373 ms (00:05.113)

PostgreSQL 10 では、追加のクエリ実行操作を並行して実行できます。並列インデックススキャンが最も一般的ですが、並列マージ結合やストアドファンクションの並列実行といった操作もあります。テーブルが大きく成長するにつれ、そのインデックスも大きくなります。大きなインデックスに対してクエリを実行する場合、並列スキャンによって結果をさらに迅速に得ることができます。

以下のクエリでは、先ほど作成したインデックスを使用して、平均販売価格のクエリを営業週にまで絞り込みます。

=> SELECT avg(o_totalprice::numeric)::numeric(10,2)
     FROM orders
    WHERE o_orderdate BETWEEN '2018-03-26' AND '2018-03-30';

    avg
-----------
 151286.04
(1 row)

Time: 1519.445 ms (00:01.519)

インデックスを使用すると、クエリは 1.5 秒で結果を獲得します。並列処理レベルを 2 に上げて、結果を 2 倍以上速く得ることができます。

=> SET max_parallel_workers_per_gather = 2;
SET
Time: 1.321 ms

=> SELECT avg(o_totalprice::numeric)::numeric(10,2)
     FROM orders
    WHERE o_orderdate BETWEEN '2018-03-26' AND '2018-03-30';

    avg
-----------
 151286.04
(1 row)

Time: 723.846 ms

これらの確立された機能を改善して、PostgreSQL 11 ではメンテナンス操作を並行して実行する機能も導入しています。たとえば、PostgreSQL 11 ではインデックスを並行して作成できます。メンテナンス操作の迅速な実行は、エンドユーザーのパフォーマンスにとっては欠かせないことです。したがって、メンテナンス操作は通常、メンテナンス時間中に実行されます。

以下のステートメントでは、customer テーブルのすべてのパーティション用のインデックスを作成しています。

=> CREATE INDEX customer_c_nationkey_c_custkey_idx 
       ON customer (c_nationkey, c_custkey);
CREATE INDEX
Time: 20850.740 ms (00:20.851)

操作は 20 秒強で完了します。このセッションではメンテナンスコマンドの並列処理レベルを 4 に上げており、このインデックスはほぼ 3 倍の速度で作成されます。

=> SET max_parallel_maintenance_workers = 4;
SET

=> CREATE INDEX customer_c_nationkey_c_custkey_idx 
       ON customer (c_nationkey, c_custkey);
CREATE INDEX
Time: 7909.719 ms (00:07.910)

JIT コンパイル

PostgreSQL 11 で提供されている新機能の 1 つに、ジャストインタイム (JIT) コンパイルがあります。JIT では、SQL 式の一部を CPU が迅速に実行できるネイティブプログラムにコンパイルします。このプログラムを生成するとコストが高くなります。JIT プログラムの生成とクエリの最適化に 1 秒以上かかるクエリもあります。クエリが最適化されると、解釈済みの SQL 式よりも迅速に実行できます。

一般的に、多くの行で計算を実行するクエリではクエリ時間が短縮されます。以下のクエリでは、3 年間の販売状況の合計売上、平均販売価格、最低売上、最高売上を計算します。

=> SELECT o_orderstatus, o_shippriority, sum(o_totalprice), avg(o_totalprice),
          min(o_totalprice), max(o_totalprice), count(*) as status_count
  FROM orders
 WHERE o_orderdate BETWEEN '2016-01-01' AND '2018-12-31'
GROUP BY 1, 2
ORDER BY 1, 2;
 o_orderstatus | o_shippriori |     sum     |       avg        |   min   | max   | status_count
---------------+--------------+-------------+------------------+---------+--------+--------------
 F             |            0 | 7.46565e+12 | 149822.502426566 | 811.73  | 586945 |     50324873
 O             |            0 | 2.07397e+12 | 146551.492403067 | 821.82  | 543957 |     14163724
 P             |            0 | 7.09769e+11 | 184767.360053128 | 1914.25 | 550128 |      3841445
(3 rows)

Time: 51036.862 ms (00:51.037)

このクエリは、約 51 秒で 3 つの集計行から結果を返します。JIT をオンにすると、クエリは同じ 3 行の結果を約 35秒で返します。

=> SET jit = on;
SET

=> SELECT o_orderstatus, o_shippriority, sum(o_totalprice), avg(o_totalprice),
          min(o_totalprice), max(o_totalprice), count(*) as status_count
  FROM orders
 WHERE o_orderdate BETWEEN '2016-01-01' AND '2018-12-31'
GROUP BY 1, 2
ORDER BY 1, 2;
 o_orderstatus | o_shippriori |     sum     |       avg        |   min   | max   | status_count
---------------+--------------+-------------+------------------+---------+--------+--------------
 F             |            0 | 7.49949e+12 | 149822.502426566 | 811.73  | 586945 |     50324873
 O             |            0 | 2.07342e+12 | 146551.492403067 | 821.82  | 543957 |     14163724
 P             |            0 | 7.0979e+11  | 184767.360053128 | 1914.25 | 550128 |      3841445
(3 rows)

Time: 35645.533 ms (00:35.646)

JIT を有効にすると、このクエリのパフォーマンスが 30% 向上します。実行に数十秒以上かかるクエリには、JIT を使用するとパフォーマンスを大幅に向上させることができます。ただし、ほとんどの一般的なクエリでは、JIT を有効にするとパフォーマンスが遅くなり、デフォルトで機能を無効にするのと同程度になります。この機能には将来性があるため、インフラストラクチャを整備して PostgreSQL の今後リリースされるバージョンで JIT の機能を改善できるようにしています。

まとめ

この記事では PostgreSQL 11 の 3 つの主な改善点、パーティション分割、並列処理、JIT コンパイルを検討しました。PostgreSQL 11 によって、ストアドプロシージャのトランザクション制御や、インデックスのカバーなどのパフォーマンスも向上します。PostgreSQL 11 の機能の完全なリストについては、PostgreSQL Version 11.1 on Amazon RDS のドキュメントをご覧ください。

PostgreSQL 11 と PostgreSQL と互換性のある Amazon Aurora を組み合わせることで、PostgreSQL コミュニティにある多くの素晴らしい機能にアクセスできるだけでなく、Aurora の持つすべてのメリットを得ることができます。Aurora では、自動フェイルオーバー、バックアップとリカバリ、自動パッチ適用、プッシュボタンのスケーリングなど、マネージド型データベース機能を提供しています。Aurora のストレージではパフォーマンスと耐久性が向上しており、このサービスには高速データベースクローニング、クエリ計画管理、クラスターキャッシュ管理といった、より高度な機能も含まれています。

PostgreSQL 12 が早くも楽しみです。パーティション分割、SQL パスや JSON パスの実装開始など、使いやすさがさらに向上することが期待できます。PostgreSQL データベースは 30 年以上のものですが、プロジェクトはさらに急速に革新を続けています。

 


著者について

Jim は AWS の主席データベースエンジニアです。AWS 入社前は OpenSCG 社の CTO であり、Postgres-XC のスケーラブルなパブリッククラウドソリューションである StormDB 社の CEO でもありました。Jim は EnterpriseDB 社のチーフアーキテクトであり、創設チームの一員でした。PostgreSQL コミュニティでも非常にアクティブに活動している Jim は、ニューヨークとフィラデルフィア両都市の PostgreSQL Meetup グループを共同で立ち上げ、最大規模の PostgreSQL カンファレンスを共同開催しています。また、PostgreSQL Foundation の理事も務めています。Jim は PostgreSQL および他のオープンソースカンファレンスでも定期的に講演しています。