PostgreSQL15の最新機能を解説してみる
スターフェスティバル株式会社 の バックエンドエンジニアの @ikkitang です。
上記の自己紹介にもありますが、私はプライベートで日本PostgreSQLユーザー会の理事をやっていて、その活動の一貫として先日の 2022-10-01(土)に行われたOpen Source Conference 2022 Online/Hiroshimaで最新バージョンのPostgreSQL15の最新機能について発表してきました。
スライドは公開しておりますので、以下を見て頂く事で全てを完全に理解していただけるかもしれませんが、解説しながらまとめたいと思います。
PostgreSQLのリリースサイクル
PostgreSQLはPostgreSQL開発コミュニティというユーザーグループ主導で開発されています。
毎年9~10月にメジャーバージョンをリリースしておりまして、今年はPostgreSQL15のリリースに向けて準備が行われておりました。(ちなみに執筆中の10月現在ではPostgreSQL16の新規開発がもう行われています)
気になるリリース日ですが、2022-10-13にPostgreSQL15のGAリリースが行われる予定です。
ちなみに、Dockerでは公式イメージでPG15のRC版をもう利用可能です! このブログで気になる機能が出てきたような方は是非試してみてください!
PostgreSQL15の新機能
今回のPostgreSQL15の新機能、本当いろんな機能が入っています。
全てを見るのは https://pgpedia.info/postgresql-versions/postgresql-15.html で見る事ができますが、自分の方でピックアップしてみます!
- Merge構文のサポート
- ロジカル(論理)レプリケーションの対象を行,列単位で絞り込める構文の追加
- パラレルクエリを
SELECT DISTINCT
構文でも有効化 - LogをJSON形式で吐き出すオプション追加
- 正規表現関数を追加
取り上げて喋りたい事は色々あるんですが、今回は Merge構文
、ロジカルレプリケーション
についてざっくり説明してみたいと思います!
Merge構文のサポート
新しくMerge構文がサポートされました。これは標準SQLで定義されているもので既にOracleやSQL Serverではサポートされていて、PostgreSQLでのサポートが待たれる所だったのですが、今回のバージョンによって扱えるようになりました。
Merge構文は何かというとInsert, Update, Deleteを一括で実行出来る構文です。
構文を見てもらうともう少し具体例がつかめるかと思いますが、基本構文は以下の通りです。
MERGE INTO {ターゲットテーブル}
USING {ソーステーブル}
ON {ターゲットとソースの結合条件}
WHEN MATCHED THEN
結合条件を満たした時の処理 (UPDATE,DELETE,DO NOTHING)
WHEN NOT MATCHED THEN
結合条件に該当しない時の処理 (INSERT,DO NOTHING);
結合条件に該当した時は UPDATE
, DELETE
, DO NOTHING (何もしない)
から処理を選ぶ事が出来ます。逆に結合条件に該当する物がなかった場合は INSERT
, DO NOTHING
のどちらかから処理を選びます。
WHEN
の条件には MATCHED
の条件の他にも ターゲット.created >= '2001-01-01'
みたいな等号・不等号の条件も記述する事が出来ます。WHEN MATCHED
句については上から順に比較して、該当した時点で処理が決まるので 優先度の高い順序から WHEN MATCHED
句を書いて頂くのが良いかと思いました。
ソーステーブルについては、既存のテーブルを条件に指定しても良いですし、値を列として解釈させる事も出来ます。
# 既存のテーブルをソーステーブルとするパターン
MERGE INTO member_logs
USING members
ON member_logs.member_id = members.member_id
# 省略
# 与えた値をソーステーブルとするパターン
MERGE INTO member_logs
USING (VALUES (1, '最終ログイン', '2022-01-01'))
AS m_source(member_id, key_name, occured)
ON member_logs.member_id = members.member_id
# 省略
Merge文はInsert,Updateを一度に行う特性からUPSERT
とグルーピングされて呼ばれてます。 UPSERTの構文はPostgreSQLには既に2種類の書き方が存在していますし、MySQLにもあります。
それぞれ何が違うのかを比較しておきたいと思います。
既存のUPSERTとの比較
これまで、PostgreSQL では INSERT ON CONFLICT
でUPSERTが実現出来ていました。(MySQLでも INSERT ON DUPLICATE KEY UPDATE
で実現出来てると思います)
項目 | Merge | Insert on conflict |
---|---|---|
処理速度 | Mergeの方が 10% ~ 100% ぐらい速い ※参考 | |
サポート |
INSERT , UPDATE , DELETE に対応 / 条件式に不等号も記述出来る |
INSERT , UPDATE に対応 |
実行の注意事項 | CONFLICTに指定するカラムにユニーク制約が必須 |
簡単にあげても上記のような差があります。
処理速度の違いについては、Merge文の仕組みを理解した上で納得すると腹落ちしやすいと思います。
PostgreSQL15 Document に以下のように記述があります。
First, the MERGE command performs a join from data_source to target_table_name producing zero or more candidate change rows.
つまり、ターゲットテーブルとソーステーブルを一度結合条件でJOINした後でMATCHED句を評価して予め処理を決めておくという実装がされています。
反面、INSERT ON CONFLICT
では 「一度INSERTを試みて、ユニーク制約違反のCONFLICTが発生したらUPDATEする」というような動きになるので先に処理を決めてから動くMERGEと実行結果によって処理が決まるINSERT ON CONFLICTの違いがあります。
その辺りのオーバーヘッドの差が処理性能に直結しているのかと思います。
では、MERGE構文は INSERT ON CONFLICTの完全上位互換なのか
という疑問が生まれるかと思いますが、そうではありません。
INSERT ON CONFLICT
は実際にINSERTを試みて、その結果に応じてそのままINSERTで良いのか、やっぱりUPDATEするのかという処理を決めます。なので、(INSERT,UPDATEどちらが流れても最終的に結果が変わらないという前提で)並行でデータがDBに複数入る場合であってもエラーが発生せずに結果が担保されます。
逆にMerge文は予め処理を決めてから実行するので、決めた後とDBのデータに違いがある とエラーとなります。
例えば、ロック開放待ちの時がそうです。予め処理を決めてからロック獲得に入るので、ロック開放待ち状態になると決めた処理と一緒にロック待ちになります。ロックが開放された時に処理を決めたデータとDBの実データに差異があってMATCHED句の評価がずれる、というときに影響があります。
実例としては クラスメソッドさんのブログの MERGE と INSERT ON CONFLICT の同時実行性の違い
のセクションで例示されてるので是非見てみてください。
PostgreSQL 15にMERGE文UPSERTがやってくる
完全に互換性のあるものでは無いので、ユースケースに応じて適切に使っていく必要があります。
ロジカル(論理)レプリケーションの機能追加
PostgreSQLにおける2つのレプリケーション
PostgreSQLには二種類のレプリケーションがあって、ストリーミングレプリケーション, ロジカルレプリケーションがあります。
ストリーミングレプリケーション
ストリーミングレプリケーション
というのは リードレプリカを使うときに使われる手法です。プライマリ(レプリケーション元)からWALというトランザクションログの情報を同期して、スタンバイ側(レプリケーション先)でWALを適用する事でインスタンスのコピーを作る事を実現出来ます。
利用用途としてリードレプリカやフェイルオーバーの仕組みを提供したりする事ができます。
完全にコピーされるのでスタンバイ側はリードオンリーにしなければならない、プライマリとスタンバイでOSやメジャーバージョンを合わせる必要があるといった制約があります。
ロジカルレプリケーション
逆にロジカルレプリケーションというのは、論理レプリケーションとも呼ばれていて、テーブル単位やデータベース単位など部分的にレプリケーションする手法です。
その仕組みから、レプリケーション元をパブリッシャー、レプリケーション先をサブスクライバーと呼びます。ストリーミングレプリケーションとの違いとして、サブスクライバー側は完全にコピーされた状態で無くても良いのでサブスクライバー側に書き込みをしても良かったり、OS・メジャーバージョンがパブリッシャー・サブスクライバー間で異なっていても良いというレプリケーションです。
仕組みとしてWALをプラグインによってデコードして 操作(INSERT/UPDATE/DELETE)と対象テーブルと値
にした後でサブスクライバー側に転送しWALを適用してレプリケーションを実現します。
(WALをデコードするイメージは この辺り 見るとイメージわくかと思います。)
WALをそのまま適用する場合は、PostgreSQLの内部構造に依存してしまうためにメジャーバージョンを合わせるなどの制約がありましたが、「対象テーブルにこんな値をINSERT/UPDATE/DELETEした」という情報であればバージョンが変わろうが適用出来る、というイメージです。
これによって、分析したいテーブルだけを論理レプリケーションして分析用途のサブスクライバーを用意したり、サブスクライバーをPostgreSQLの新メジャーバージョンにして 旧->新の論理レプリケーションを組む事で安全にバージョンアップをすすめるなども可能です。
PostgreSQL15のロジカルレプリケーションの新機能
ロジカルレプリケーションでこれまでは、テーブル単位 / データベースに含まれるテーブル全て といった指定が可能でしたが スキーマ内のテーブル全て
を対象に論理レプリケーション対象とする事が出来ました。
CREATE PUBLICATION app_schema FOR ALL TABLES IN SCHEMA app;
また、論理レプリケーションの対象についてテーブルよりもっと小さい粒度である列や行を絞った上での論理レプリケーションが出来るようになりました。
# 行の絞り込み
CREATE PUBLICATION chugoku_members FOR TABLE app.members WHERE address IN ('広島','岡山','島根','鳥取','山口');
# 列の絞り込み
CREATE PUBLICATION member_emails FOR TABLE app.members (email);
よりデータ量が多い場合に対応出来たりするんでは無いでしょうか。
まとめ
- PostgreSQL15では、以下のようなアップデートがあった
- Merge構文や正規表現関数など他RDB製品でサポートされていた機能を積極サポートした
- ロジカルレプリケーションなどの既存の強みについても更に強化をした
- ※ スタフェスではPostgreSQLではなく、MySQLを採用しています。
弊社採用頑張っています
最近、幸いな事に採用によりエンジニアの方が沢山入ってくださっています。
15年弱業務を支え続けてきたアプリケーションについては課題が山程あり、現在心強い仲間達と共に業務フローを見直しつつシステムを再設計しながら課題解決を行っています。「PostgreSQLではなく、MySQLを使ってます」という所も解決すべきはDB設計だけにとどまらずもっと根本から改善する必要がある為という背景もちゃんとあります!笑
どんな事やってるの?という方向けに カジュアル面談もさせて頂いていて、 @ikkitang 宛に DM とかでご連絡頂ければ対応させてもらいますので、是非とも!
私のようにPostgreSQLを背景に持つ人もいれば、モバイルアプリのゲーム開発に携わってきたエンジニアや設計大好きエンジニアなど、色々なバックボーンを持つエンジニアが在籍していて、プログラミングの時に吸収出来るポイントが非常に多いなと感じます。
カジュアル面談前に雰囲気を掴んでみたい、という方には こちらの スタフェス公式 Note とかで プレイヤーレベルの方のインタビューや経営層の方のインタビューなどが掲載されているので見て頂くと良いかもです。
また、先日 オンラインでスタフェスMeetupを開催させて頂きまして、下記リンクから当日の様子を確認することも出来ますので、興味を持っていただければ、ご確認ください。
Discussion