現在位置: ホーム / OSSブログ / PostgreSQL 9.4 新機能の概要 (1)

PostgreSQL 9.4 新機能の概要 (1)

PostgreSQL9.4のリリースは秋頃ということですので、近いうちにリリースがされることが予想されます。既にリリースされているBeta版からPostgreSQL 9.4に予定されている主な新機能を二回に分て一通り紹介します。

ベータ版、開発版の情報を元に記事を作成しています。リリース版では差異がある可能性があります。予めご了承ください。

PostgreSQL 9.4新機能の一覧

重要な機能追加

  • レプリケーションスロット:レプリケーション元にレプリケートした位置を保存する機能
  •  論理デコーディング: レプリケーションデータを論理的な値として扱う機能
  • JSONB型の追加:JSONデータをハッシュとして取り扱うデータ型

パフォーマンス改善

  • GINインデックス:小く&速く
  • pg_prewarm:事前ウォームアップ

その他の変更

  • ALTER SYSTEM:SQLからサーバー設定を変更
  • REFRESH MATERIALIZED VIEW CONCURRENTLY:マテリアライズドビュー更新の改善
  • 更新可能ビュー改善:利用可能なクラス(テーブル)を追加
  • WITH CHECK OPTION:更新可能ビューで参照不可となる挿入/更新の制限
  • 更新可能なセキュリティバリアービュー:security_barrier = true も更新可能ビューに
  • WITH ORDINALITY:関数の戻り値に連番を付与

集合機能

  • オーダーセット集合:順序を考慮した集合機能
  • FILTER:集合関数のパラメーターをフィルター
  • 移動集合:前の集合関数の結果を再利用
  • state_data_size パラメーター:CREATE AGGRATEに渡すパラメーター
  • NUMERICの集合:NUMERIC型の集合関数の性能改善

 

レプリケーションスロット

9.4以前のレプリケーションでは、レプリケーション元(プライマリ)はレプリケーション先(スレーブ)がレプリケートが完了した箇所を通知する機能がありませんでした。このため、レプリケーション遅延が発生した場合、レプリケーション元ではどの程度のWALを保存すべきであるか判断ができませんでした。

レプリケーションスロットを利用するとマスター側でレプリケーションが完了した部分を特定でき、必要なWALを必要なくなるまで保持できるようになります。

利用方法

プライマリ側の設定(postgresql.conf)

  max_replication_slot = 1  # 1以上
  wal_level = archive  # archive以上。hot_standbyなど

プライマリ再起動後にレプリケーションスロット名を設定

  # SELECT * FROM pg_create_physical_replication_slot('standby_replication_slot');

スレーブ側の設定(postgresql.conf) - プライマリに設定したレプリケーションスロット名を指定

  primary_slotname = 'standby_replication_slot'

プライマリ側からレプリケーションスロットが利用されていることを確認

 # SELECT * FROM pg_replication_slots;
  slot_name          | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn
  --------------------------+--------+-----------+--------+----------+--------+------+--------------+-------------
  standby_replication_slot |        | physical  |        |          | t      |      |              | 0/3000420
  (1 row)

論理デコーディング

PostgreSQLのレプリケーションはWAL(Write Ahead Log)をプライマリからスレーブにコピーすることにより実現されています。論理デコーディングはログの塊でなく、SQL文とデータを一つ一つ論理的にレプリケーションするために必要な機能です。

論理的なレプリケーションが可能になると以下の機能の実現が可能になります。

  • 異るPostgreSQLバージョンのレプリケーション
  • 異るデータベースへのレプリケーション
  • 一部のデータベースのレプリケーション
  • スレーブ側への書き込み
  • マルチマスターレプリケーション

PostgreSQL 9.4でこれらの機能が実現しませんが、上記機能を実装する基盤が実装された事になります。例えば、PostgreSQL 9.xから10.xなどへのマイグレーションなどが可能になります。7x24運用を基本としているサービスを提供している利用者にとっては非常に大きなメリットになります。

実装が生まれるかどうかは分かりませんが、他のデータベースシステムであっても、PostgreSQLが理解可能なデータを送信できればレプリケーション可能になります。PostgreSQLから他のデータベースにレプリケーションする事も可能になります。

スレーブ側で書き込みが可能になると、スレーブ側のみに必要となる機能を実装することも可能になります。例えば、PostgreSQLはORDB機能を持っています。ORDB機能を利用してテーブルを継承可能になりますが、継承テーブルはそれぞれインデックスを持つ必要があります。スレーブ側のみ継承元テーブルにインデックスを作成すれば、普段使っているDBは高速かつディスクスペースを節約しつつ、スレーブ側では必要なインデックスも作成してデータ分析を高速化する、といった使い方も可能になります。※

※ ORDB機能にはOIDが必要です。PostgreSQLのOIDは32bit整数なので、大きなDBではOIDが64bit化されないと実用的でない場合も多い。

論理デコーディングのテスト方法


プライマリ側の設定(postgresql.conf)

  wal_level = logical
  max_replication_slots = 1

設定変更後にPostgreSQLを再起動します。

PostgreSQL 9.4にはテスト用デコーダー実装があります。PostgreSQL 9.4をソースからインストールしている場合、contrib/test_decodingディレクトリからビルドしてインストールします。

  # cd postgresql-9.4beta1/contrib/test_decoding
  # make && make install

これでtest_docdingが利用可能になります。

  postgres=# SELECT * FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
    slotname     | xlog_position
  -----------------+---------------
CC | 0/171C1A0
  (1 row)

作ったレプリケーションスロットはpg_replication_slotsで確認できます。

  postgres=# SELECT * FROM pg_replication_slots;
  slot_name    |    plugin     | slot_type | datoid | database | active | xmin  | catalog_xmin | restart_lsn
  -----------------+---------------+-----------+--------+----------+--------+------+--------------+-------------
  regression_slot | test_decoding | logical   |  13046 | postgres | f      |   |         1890 | 0/171C168
    (1 row)

レプリケーションスロットに蓄積されているデータはpg_logical_slot_get_changes()で参照します。

  postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
  location | xid | data
  ----------+-----+------
  (0 rows)

レプリケーションスロットには、まだレプリケーションされていないデータが蓄積されます。この例の環境の場合、スレーブがないので無限に蓄積されることになります。レプリケーションスロットのデータはVACCUMでは解放されません。不必要になったレプリケーションスロットは削除しないとディスク領域を圧迫することになります。

  postgres=# CREATE TABLE mytable (id serial primary key, mydata text);  
  CREATE TABLE
  postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
  location  | xid  |    data
  -----------+------+-------------
  0/171C3E0 | 1890 | BEGIN 1890
  0/1737C00 | 1890 | COMMIT 1890
  (2 rows)

テーブルにデータを挿入した場合の変更情報は以下のようになります。

  postgres=# INSERT INTO mytable (mydata) VALUES ('TEST DATA1');
  INSERT 0 1
  postgres=# INSERT INTO mytable (mydata) VALUES ('TEST DATA2');
  INSERT 0 1
  postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
  location  | xid  |                                 data
 
  -----------+------+-----------------------------------------------------------------------
  0/1737E48 | 1891 | BEGIN 1891
  0/1737E48 | 1891 | table public.mytable: INSERT: id[integer]:1 mydata[text]:'TEST DATA1'
  0/1737F98 | 1891 | COMMIT 1891
  0/1737F98 | 1892 | BEGIN 1892
  0/1737F98 | 1892 | table public.mytable: INSERT: id[integer]:2 mydata[text]:'TEST DATA2'
  0/1738088 | 1892 | COMMIT 1892
  (6 rows)

ここでテーブルを作成した時のデータがない事に気付くと思います。pg_logical_slot_get_changes()はレプリケーションスロットからデータを取得しているので、取得済のデータは解放されていることが分かります。

postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
  location | xid | data
  ----------+-----+------
  (0 rows)

レプリケーションスロットの内容を変更せずにデータを取得するにはpg_logical_slot_peek_changes()を利用します。

  postgres=# INSERT INTO mytable (mydata) VALUES ('TEST DATA3');INSERT 0 1
  postgres=# SELECT * FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL);
  location  | xid  |                                 data
 
  -----------+------+-------------------------------------------------------------
  ----------
  0/1738258 | 1893 | BEGIN 1893
  0/1738258 | 1893 | table public.mytable: INSERT: id[integer]:3 mydata[text]:'TEST DATA3'
  0/1738448 | 1893 | COMMIT 1893
  (3 rows)
postgres=# SELECT * FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL);
  location  | xid  |                                 data
  -----------+------+-------------------------------------------------------------<
  ----------
  0/1738258 | 1893 | BEGIN 1893
  0/1738258 | 1893 | table public.mytable: INSERT: id[integer]:3 mydata[text]:'TEST DATA3'
  0/1738448 | 1893 | COMMIT 1893
  (3 rows)
postgres=# SELECT * FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL);
  location | xid | data
  ----------+-----+------
  (0 rows)

JSONB型

より効率的にJSONデータを取り扱うJSONB型が導入されました。JSON型はテキスト形式でデータが保存されていますが、JSONB型はバイナリ(ハッシュ)として保存されています。多少、使い勝手が異なりますが、JSONB型の方が効率がよいです。

JSONB型については詳しく紹介済みです。こちら(リンクをお願いします)を参照してください。

小く速いGINインデックス

GINの使い方は変りませんが、小く、高速になりました。同時にB-Treeのようなクラッシュリカバリーも実装されています。

GINインデックスは同類のインデックスであるGiSTに比べ、一般に

と言われていました。PostgreSQL 9.4からはこの常識が変わります。


PostgreSQL 9.4のGIN

  • インデックスサイズが1/5 (になる場合もある)
  • 稀および頻繁なクエリが高速化

開発者の発表によるとPostgreSQL 9.3で58MBだったインデックスサイズが11MBまで減少したと報告しています。同じB-Treeインデックスのサイズは214MBであったとしています。

このことからデータベース設計においてGINインデックスを活用すると、好ましい結果を得られるケースが増えると考えられます。

  • btree_gin拡張を利用しGINをステータスフィールドに利用
  • ビットマップインデックスの代わりにGINを利用

すべてのケースでGINでB-Treeを代用すべきではなく、ステータスなど重複が多いデータに対して利用すると検索性はそのままで、インデックスサイズを1/20などに縮小することが可能になります。

ウォームアップでサービス提供開始から最速状態で運用

データベースを再起動するとバッファがクリアされ、起動直後のデータベース性能が低下します。これを防ぐ為に

find $PGDATA/base -type f -exec cat {} +

などを実行してからサービス提供を開始していか方もいると思います。しかし、これでは特定のテーブルデータのみをプレウォームできません。その為には詳しいデータベースファイルの構造を知る必要がありました。

pg_prewarm関数を利用すると簡単に特定テーブル(クラス)をバッファにロードできます。

関数定義

  pg_prewarm(regclass, mode text default 'buffer', fork text default 'main',
  first_block int8 default null,
  last_block int8 default null) RETURNS int8

引数

  • pgclass: プレウォーム対象
  • mode: プレウォーム方法(prefetch, read, buffer - デフォルト: buffer)
  • fork: プレウォームされるリレーションフォーク(main, fsm, vm - デフォルト: main)
  • first_block: 最初のブロック
  • last_block: 最後のブロック

戻り値

  • プレウォームされたブロック数

色々オプションがありますが、基本的には最初のプレウォーム対象であるテーブル名を指定するだけで利用できます。

  $ SELECT pg_prewarm('mytable');

すべてのテーブルがメモリにキャッシュできる場合はすべてプレウォームしても構いませんが、メモリに収まりきらない場合、OSは最初に読み込んだキャッシュを破棄するでしょう。メモリとデータサイズを考慮して利用しなければならない点は変わりません。

まとめ

今回はPostgreSQL 9.4の大きな新機能を中心に紹介しました。PostgreSQL 9.4がリリースされても論理デコーディングはすぐに有用となる機能ではありませんが、将来が楽しみになる機能です。

次回はSQL関連の機能追加を中心に解説します。

参考URL