話題の Google Cloud の新しい DB の AlloyDB for PostgreSQL を調査して、分析クエリ高速化機能のカラム型エンジンを試してみた
ウィスキー、シガー、パイプをこよなく愛する大栗です。
Google I/O 2022 で新しいデータベースである AlloyDB for PostgreSQL が発表されました。実際にサービスを触ってみたのでまとめてみます。
AlloyDB for PostgreSQL は2022年5月18日現在において、プレビューのステータスです。このプロダクトまたは機能は、Google Cloud Platform の利用規約の一般提供前のサービス規約の対象となります。一般提供前のプロダクトと機能では、サポートが制限されることがあります。また、一般提供前のプロダクトや機能に変更が加えられると、他の一般提供前バージョンと互換性がない場合があります。詳細については、リリースステージの説明をご覧ください。
AlloyDB for PostgreSQL とは
AlloyDB for PostgreSQL は PostgreSQL と互換性を持つエンタープライズグレードで、標準的な PostgreSQL と比較して、トランザクションで 4 倍、分析クエリで最大 100 倍の性能を発揮するデータベースです。
AlloyDB の特徴
- 優れたパフォーマンスと拡張性
商用グレードのワークロードに対して、優れたパフォーマンスとスケールを提供します。標準的な PostgreSQL と比較して 4 倍、AmazonのPostgreSQL 互換サービスと比較して 2 倍の速度でトランザクションを処理できると主張しています。ここで言われているAmazonのPostgreSQL 互換サービス
は Amazon Aurora PostgreSQL 互換エディションを指していると思われます。 - 業界トップクラスの可用性
(現在はプレビューであるため SLA は提供されていませんが)メンテナンスを含めて 99.99% の高可用性 SLAを実現します。大半の障害を自動で検出して、数秒以内に回復します。 - リアルタイムのビジネスインサイト
標準的な PostgreSQL と比較して最大 100 倍高速な分析クエリを実現します。カラム型でデータをメモリに格納し高速なスキャンや集計を実現します。トランザクションと分析を同じデータで処理する HTAP を実現します。 - 予測しやすく明朗な価格設定
高額な独自ライセンスが不要で、I/O 課金が無いため、予測しやすく明朗な価格設定になっています。インスタンスのメモリに加えて自動的にプロビジョニングされる無料の超高速キャッシュによりコストパフォーマンスを高めます。 - 機械学習による管理とインサイト
AlloyDB はデータベースのパッチ適用、バックアップ、スケーリング、レプリケーションを自動的に処理しますが、適応型アルゴリズムと機械学習により PostgreSQL の バキューム管理、ストレージとメモリ管理、データ階層化、分析高速化は更に進んでいます。また Google Cloud の AI プラットフォームである Vertex AI と統合されます。
AlloyDB の構成
AlloyDB の構成概要は以下のようになっています。AlloyDB は全体をクラスタという概念でまとめており、一つのクラスタ ストレージを持ち、プライマリインスタンスと複数の読み取りプールノードを持ちます。各インスタンスは 1 つのクラスタ ストレージを共有しており、プライマリインスタンスは書き込みと読み取りが可能で、読み取りプールノードは読み取り専用となっています。またプライマリインスタンスはフェイルオーバー専用のレプリカを持っています。
Introducing AlloyDB, a PostgreSQL-compatible cloud database service https://www.youtube.com/watch?v=o5pKfH0Tonwより引用
AlloyDB のストレージ
AlloyDB のストレージは Google の分散ファイルシステムである Colossus1の上に実装されており、先行書き込みログ (WAL) を書き込むと、ストレージレイヤーでログ処理サービス (LPS) が WAL レコードを処理してマテリアラズされたデータベースブロックを生成して、障害に強くシャーディングされたリージョナルブロックストレージに書き込みます。
AlloyDB for PostgreSQL under the hood: Intelligent, database-aware storage
https://cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storageより引用
通常の PostgreSQL は、更新処理で Commit を行った時に WAL をストレージへ書き込み、定期的なチェックポイント処理で共有バッファのダーティページをストレージへ書き込み、不要になった WAL をストレージから削除するというディスクアクセスが必要となります。AlloyDB ではインテリジェントなストレージレイヤーにより、書き込みを行うプライマリインスタンスは WAL の書き込みだけを行い、それ以降のダーティページの書き込みや不要になった WAL の削除はストレージ側で実施されます。
インテリジェントなストレージレイヤーをコンピュートリソースと分離するアプローチにより、以下のようなメリットがあります。
- コンピュートとストレージを各々にスケールさせることが可能になる
- ストレージレイヤーでの複数ゾーンの冗長化でゾーン障害からシステムを保護する
- 更新時に WAL レコードのみストレージレイヤーへ通信してネットワークを効率的に利用できる
- 低レイテンシなリージョナルログストレージにより高負荷時でも高いパフォーマンスを発揮する
- リードレプリカ作成時にデータコピーが不要となり高速に作成できる
- ストレージレイヤーで継続的に WAL を再生するためリカバリ時の WAL 適用が最小になりシステムの再起動が高速になる
- バックアップ操作がストレージレイヤで処理され、データベースレイヤーのパフォーマンスやリソースへ影響を与えない
AlloyDB のカラム型エンジン
AlloyDB ではカラム型エンジンにより分析クエリを高速に処理できます。カラム型エンジンはインスタンスのメモリ上で行フォーマットデータを AI/ML によって自動的にカラム型フォーマットへ変換します。またインスタンスのメモリや超高速キャッシュと複数レイヤーのキャッシュによって高速なパフォーマンスを発揮します。
各インスタンスのメモリ上でカラム型フォーマットを実現しており、インスタンスごとに設定を変えられるので、インスタンスごとにトランザクションワークロードと分析ワークロードを分けることも可能になります。
AlloyDB for PostgreSQL under the hood: Intelligent, database-aware storage
https://cloud.google.com/blog/products/databases/alloydb-for-postgresql-intelligent-scalable-storageより引用
カラム型ストアに追加できるデータには制限があります。
- 以下の組み込みデータ型の列のみサポートされます。
- bigint, char, date, decimal, double precision, float4, float8, integer, numeric, real, serial, short, smallint, text, timestamp, toast, varchar
- サポートされていないデータ型は、手動でカラム型ストアに追加しようとすると無視されます。
- リーフでないパーティションテーブルは、データソースとしてサポートしていません。
- 外部テーブルはデータソースとしてサポートされていません。
- 5,000 行未満のテーブルは、データソースとしてサポートされません。
利用可能なリージョン
2022年5月18日時点で、以下のリージョンで AlloyDB for PostgreSQL を利用できます。
- asia-northeast1 (東京)
- asia-south1 (ムンバイ)
- asia-southeast1 (シンガポール)
- australia-southeast1 (シドニー)
- europe-central2 (ワルシャワ)
- europe-west1 (ベルギー)
- europe-west2 (ロンドン)
- europe-west3 (フランクフルト)
- europe-west4 (オランダ)
- southamerica-east1 (サンパウロ)
- us-central1 (アイオワ)
- us-east4 (北バージニア)
- us-west3 (ソルトレイクシティ)
料金
AlloyDB の料金は、CPU と メモリの料金、ストレージの料金、ネットワークの料金で構成されます。なお、プレビュー期間中は AlloyDB を無料で利用できます。
AlloyDB for PostgreSQL pricing
CPU と メモリの料金
AlloyDB のコンピューティングリソースは vCPU とメモリ量で料金が設定されます。料金ページには zonal primary instance に言及がありますが、2022年5月18日現在プライマリインスタンスはリージョナルインスタンスのみであるため、プライマリインスタンスは2倍の料金がかかると考えておけばいいと思います。
項目 | アイオワ(us-central1) | 東京(asia-northeast1) |
---|---|---|
vCPU | 1 vCPU あたり $0.06608 | 1 vCPU あたり $0.08458 |
メモリ | $0.0112/GB | $0.01434/GB |
現在選択可能な vCPU とメモリ量でアイオワと東京の月額料金を算出すると以下になります。月額は 730 時間換算です。
マシンタイプ | 1ヶ月:アイオワ(us-central1) | 1ヶ月:東京(asia-northeast1) |
---|---|---|
4 vCPU、32 GB | $454.5856/月 | $581.956/月 |
8 vCPU、64 GB | $909.1712/月 | $1163.912/月 |
16 vCPU、128 GB | $1818.3424/月 | $2327.824/月 |
32 vCPU、256 GB | $3636.6848/月 | $4655.648/月 |
64 vCPU、512 GB | $7273.3696/月 | $9311.296/月 |
ストレージの料金
AlloyDB はクラスタに対してストレージが設定されるため、読み取りプールが増加してもストレージ料金に影響しません。AlloyDB は実際に使用したストレージに対して料金を支払います。
項目 | 1時間:アイオワ(us-central1) | 1ヶ月:アイオワ(us-central1) | 1時間:東京(asia-northeast1) | 1ヶ月:東京(asia-northeast1) | |
---|---|---|---|---|---|
Regional cluster storage | $0.0004109/GB | $0.3/GB | $0.000526/GB | $0.38/GB |
ネットワークの料金
Northern America | Europe | Asia | Indonesia | Oceania | Latin America | |
---|---|---|---|---|---|---|
Northern America | $0.02/GB | |||||
Europe | $0.05/GB | $0.02/GB | ||||
Asia | $0.08/GB | $0.08/GB | $0.08/GB | |||
Indonesia | $0.10/GB | $0.10/GB | $0.10/GB | N/A | ||
Oceania | $0.10/GB | $0.10/GB | $0.10/GB | $0.08/GB | $0.08/GB | |
Latin America | $0.14/GB | $0.14/GB | $0.14/GB | $0.14/GB | $0.14/GB | $0.14/GB |
制限
現時点で以下の上限が設定されています。
項目 | 上限 |
---|---|
リージョンごとプロジェクトごとのクラスタ数 | 3 |
リージョンごとプロジェクトごとの vCPU | 1,100 |
クラスタごとの読み取りプールノード | 20 |
クラスタごとのデータベースストレージ | 500 GB(GA では 64 TB) |
インスタンス当たりの最大同時接続数 | マシン対応に基づきmax_connections を設定 |
プレビュー中は無料での利用となり公正な使用制限がされており、以下の月間使用制限があります。
- コンピュート:15,000 USD の使用量、us-central1 における最低 125 vCPU および 1,000 GB の RAM に相当。
- ストレージ:650 USD の使用量、us-central1 における 2TB 以上に相当
やってみる
AlloyDB クラスタの作成
まずは Cloud Console で AlloyDB for PostgreSQL のメニューを開きます。初めて AlloyDB を触る場合には API の有効化が必要なのでENABLE
をクリックして有効化します。また、Compute Engine、Resource Manager、Service Networking の各種 API も無効の場合は有効化しておきます。
上か下のCREATE CLUSTER
をクリックして AlloyDB クラスタを作成していきます。
ここではクラスタとプライマリインスタンスだけ作成するのでHighly available
を選択してCONTINUE
をクリックします。
以下のように設定値を入力します。Network で選択した VPC がプライベート サービス コネクトを設定していない場合はSET UP CONNECTION
をクリックして設定を行います。
項目 | 設定値 | 備考 |
---|---|---|
Cluster ID | 任意 | ここでは test-cluster-1 を設定 |
Password | 任意 | |
Region | asia-northeast1(Tokyo) | |
Network | (任意の VPC) |
プライベート サービス コネクトを設定していない場合、以下の設定を行います。ここでは IP アドレスのレンジを自動割り当てにしたので Allocate an IP range でUse an automatically allocated IP range
を選択して、CONTINUE
、CREATE CONNECTION
とクリックしてプライベート サービス コネクトを作成します。
Instance ID に任意の名称を入力し、マシンタイプを選択してCREATE CLUSTER
をクリックします。ここでは最低スペックの4 vCPU, 32 GB
を選択しました。必要に応じて Flag を設定します。
クラスタとプライマリインスタンスが起動するまでしばらく待ちます。Cloud SQL 等とは異なり、クラスタというリソースがあることに注意してください。クラスタを作成して、その上に別途プライマリインスタンスや読み取りノードプールを作成していきます。
ログインと基本動作の確認
しばらくするとプライマリインスタンスが作成されるので Private IP を確認します。
psql でログインしてみます。ここではクライアント OS は Debian 10 です。
$ PGPASSWORD=<password> psql -U postgres -h <IP Address> psql (14.3 (Debian 14.3-1.pgdg100+1), server 14.2) SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) Type "help" for help. postgres=>
デフォルト状態での拡張機能を確認すると PL/pgSQL のみが入っています。他に拡張機能が必要な場合はドキュメントに記載のある範囲で導入できます。
postgres=> SELECT * FROM pg_extension; oid | extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition -------+---------+----------+--------------+----------------+------------+-----------+-------------- 13766 | plpgsql | 10 | 11 | f | 1.0 | | (1 row)
フェイルオーバーを確認してみます。フェイルオーバーはプライマリインスタンスがスタンバイと切り替わりますが、IP アドレスは変わりません。
以下のようなスクリプトを作成します。
#! /bin/bash while true do (echo 'select now()' | PGPASSWORD=password psql -U postgres -h $1 -t) & sleep 0.5 done
引数にプライマリインスタンスの IP アドレスを引数にして実行します。
bash ./test.sh <IP Address> 2022-05-17 10:27:19.777953+00 2022-05-17 10:27:20.278747+00
プライマリインスタンスのメニューからFailover
をクリックします。
プライマリインスタンス名を入力してTRIGGER FAILOVER
をクリックします。
フェイルオーバー中は以下の様なメッセージが表示されます。
psql: error: connection to server at "10.34.80.2", port 5432 failed: Connection refused Is the server running on that host and accepting TCP/IP connections? psql: error: connection to server at "10.34.80.2", port 5432 failed: Connection refused Is the server running on that host and accepting TCP/IP connections? psql: error: connection to server at "10.34.80.2", port 5432 failed: FATAL: the database system is starting up psql: error: connection to server at "10.34.80.2", port 5432 failed: FATAL: the database system is starting up
しばらくすると正常にアクセスできるようになります。大半のデータベース障害を自動的に検出し、数秒以内に回復するとなっていますが、プレビューの段階ではもう少し時間がかかるようなので、GA までに高速化されることを期待します。
2022-05-17 10:34:34.801716+00 2022-05-17 10:34:35.098082+00 2022-05-17 10:34:35.313894+00
読み取りプールノードの追加
次に読み取りプールノードを追加してみます。
Cloud Console に戻り、+ ADD READ POOL
またはADD READ POOL INSTANCE
をクリックします。
Read pool instance ID は任意ですが、ここでは read1 としています。Node count は冗長性を考えて 2 台にします。マシンタイプは選択できる最小の 4 vCPU, 32 GB を選択しました。これでCREATE READ POOL
をクリックします。なお、選択できない 2 vCPU, 16 GB は現在開発中のようです。
読み取りプールノードが作成されるまでしばらく待ちます。
複数ノードを起動しているため read1 の High availability がHighly available (multiple read nodes)
となります。そして Private IP を確認します。
読み取りプールノードにログインします。
$ PGPASSWORD=<password> psql -U postgres -h <IP Address> psql (14.3 (Debian 14.3-1.pgdg100+1), server 14.2) SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) Type "help" for help. postgres=>
レプリケーションの情報を確認するためにリカバリ情報関数の内容を確認してみます。読み取りノードプールはストレージレベルでのデータ共有をしつつ、以下のように PostgreSQL 自体のレプリケーション機構も使用していることが分かります。読み取りノードプール側のキャッシュに未反映のデータに対するレプリケーションなどを行っているのではないでしょうか。
postgres=> SELECT pg_is_in_recovery(); pg_is_in_recovery ------------------- t (1 row) postgres=> SELECT pg_last_wal_receive_lsn(); pg_last_wal_receive_lsn ------------------------- 0/5B0048A0 (1 row) postgres=> SELECT pg_last_wal_replay_lsn(); pg_last_wal_replay_lsn ------------------------ 0/5B004950 (1 row) postgres=> SELECT pg_last_xact_replay_timestamp(); pg_last_xact_replay_timestamp ------------------------------- 2022-05-17 07:23:44.883908+00 (1 row)
カラム型エンジンの設定
カラム型エンジンを設定してみます。
プライマリインスタンスのメニューからEdit
をクリックします。
Flags のADD FLAG
をクリックします。
google_columnar_engine.enabled
を選択して値をon
に設定して、UPDATE INSTANCE
をクリックします。更新するとインスタンスが再起動します。
再起動後にプライマリインスタンスにログインします。検証用データベースを作成して、google_columnar_engine
拡張機能を作成します。
postgres=> CREATE DATABASE test1; CREATE DATABASE postgres=> \c test1 psql (14.3 (Debian 14.3-1.pgdg100+1), server 14.2) SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) You are now connected to database "test1" as user "postgres". test1=> CREATE EXTENSION IF NOT EXISTS google_columnar_engine; CREATE EXTENSION
拡張機能の一覧を見てgoogle_columnar_engine
が増えていることを確認します。
test1=> SELECT * FROM pg_extension; oid | extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition -------+------------------------+----------+--------------+----------------+------------+-----------+-------------- 13766 | plpgsql | 10 | 11 | f | 1.0 | | 24581 | google_columnar_engine | 16390 | 2200 | t | 1.0 | | (2 rows)
検証用データを作成します。以下のエントリのTPROC-H の実施を参考にサンプルデータを作成してベンチマークも実行します。
diset tpch pg_tpch_dbase
でtest1
を、diset tpch pg_scale_fact
で10
を変更しています。
データを作成したら以下のような行数になりました。
テーブル | 行数 |
---|---|
customer | 1500000 |
lineitem | 60006054 |
nation | 25 |
orders | 15000000 |
part | 2000000 |
partsupp | 8000000 |
region | 5 |
supplier | 100000 |
分析クエリを実施して統計情報が収集されたのでgoogle_columnar_engine_run_recommendation
関数でカラムストアのサイズの推奨設定を確認します。インスタンスのメモリサイズの半分の MB の値を入力します。メモリサイズが 32 GB なので、32 / 2 * 1024
= 16384
となります。実施すると以下のように出力されます。
test1=> SELECT google_columnar_engine_run_recommendation( test1(> 16384, test1(> 'PERFORMANCE_OPTIMAL' test1(> ); google_columnar_engine_run_recommendation ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- (4455,"test1.public.customer(c_acctbal:1,c_address:1,c_comment:1,c_custkey:1,c_mktsegment:1,c_name:1,c_nationkey:1,c_phone:1),test1.public.lineitem(l_commitdate:1,l_discount:1,l_extendedprice:1,l_linestatus:1,l_orderkey:1,l_partkey:1,l_quantity:1,l_receiptdate:1,l_returnflag:1,l_shipdate:1,l_shipinstruct:1,l_shipmode:1,l_suppkey:1,l_tax:1),test1.public.orders(o_comment:1,o_custkey:1,o_orderdate:1,o_orderkey:1,o_orderpriority:1,o_orderstatus:1,o_shippriority:1),test1.public.part(p_brand:1,p_container:1,p_name:1,p_partkey:1,p_size:1,p_type:1),test1.public.partsupp(ps_partkey:1,ps_suppkey:1,ps_supplycost:1),test1.public.supplier(s_address:1,s_name:1,s_nationkey:1,s_suppkey:1)")
プライマリインスタンスでgoogle_columnar_engine.memory_size_in_mb
を設定します。プライマリインスタンスのメニューからEdit
をクリックします。
Flags のADD FLAG
をクリックします。
google_columnar_engine.memory_size_in_mb
を選択して確認した推奨値の4455
に設定してDONE
をクリックして、UPDATE INSTANCE
をクリックします。更新するとインスタンスが再起動します。
プライマリインスタンスにログインして、カラム型エンジンのレコメンドを手動で適用します。
test1=> SELECT google_columnar_engine_run_recommendation(0, 'FIXED_SIZE', TRUE);
カラム型ストアに追加された項目を確認します。以下のように自動でレコメンドされたものが設定されています。
test1=> SELECT * FROM g_columnar_relations; database_name | schema_name | relation_name | status | size | uncompressed_size | columnar_unit_count | invalid_block_count | block_count_in_cc | total_block_count | auto_refresh_trigger_count | auto_refresh_failure_count | auto_refresh_recent_status ---------------+-------------+---------------+--------+------------+-------------------+---------------------+---------------------+-------------------+-------------------+----------------------------+----------------------------+---------------------------- test1 | public | partsupp | Usable | 138477513 | 138477513 | 45 | 0 | 183224 | 183224 | 0 | 0 | NONE YET test1 | public | supplier | Usable | 7311932 | 7311932 | 1 | 0 | 2264 | 2264 | 0 | 0 | NONE YET test1 | public | orders | Usable | 1312378329 | 1312378329 | 68 | 0 | 278516 | 278516 | 0 | 0 | NONE YET test1 | public | part | Usable | 119587441 | 119587441 | 11 | 0 | 41919 | 41919 | 0 | 0 | NONE YET test1 | public | lineitem | Usable | 2957139448 | 2957139448 | 325 | 0 | 1330412 | 1330412 | 1 | 0 | SUCCESS (5 rows)
カラム型エンジンのレコメンドを、手動ではなく定期的に自動追加するポリシーを作成する場合は以下のクエリを実行します。ここでは 1 時間ごとにレコメンドの適用を実施します。
test1=> SELECT google_columnar_engine_add_policy( test1(> 'RECOMMEND_AND_POPULATE_COLUMNS', test1(> 'EVERY', 1, 'HOURS' test1(> ); google_columnar_engine_add_policy ----------------------------------- t (1 row)
カラム型エンジンはメモリ上で動作するものなので、利用する場合はメモリサイズが大きいマシンスペックを選択すべきだと思われます。
クエリ状況の把握やモニタリング
AlloyDB にも Cloud SQL と同様に Query Insights があり、実行されたクエリの状況を把握できます。ここでは HammerDB で実行した TPC-H の派生ベンチマークである TPROC-H のクエリが表示されています。
CPU やメモリ、接続数やトランザクション数などモニタリングも AlloyDB のコンソールから可能です。
バックアップ
バックアップを実施します。
Backups のメニューでCREATE BACKUP
をクリックします。
対象のクラスタを選択しバックアップ ID を入力してCREATE
をクリックします。
バックアップが作成されます。
バックアップはストレージレイヤーで実施される機能なのですが、なぜかプライマリインスタンスを削除してクラスタだけの状態だとバックアップがエラーとなります。バックアップはストレージレイヤーで完結する機能なので、GA までにクラスタ単体でもバックアップが可能になってほしいです。
さいごに
AlloyDB は高速なトランザクションだけでなく分析クエリも高速に実行できるのが特徴です。分析クエリのためのカラム型エンジンの適用は個別の設定を考える必要がなく、統計情報を自動的に判断してくれるため運用負荷も少なくなっています。現時点ではDatabase Migration Serviceによる Oracle から PostgreSQL への変換はプレビューですが AlloyDB for PostgreSQL へも活用できると思われます。商用データベースからの移行先として重要なサービスになるため、今後も注視していきたいと思います。
また利用規約上ベンチマーク結果を公表できないため、本エントリでは速度や時間について具体的な数字に触れておりません。具体的なパフォーマンスを確認されたい場合は、エントリ内の手順や以下のベンチマーク取得エントリを参考にご自身で確認してみてください。
- Colossus の詳細はColossus の仕組み: Google のスケーラブルなストレージ システムの舞台裏が詳しいです。 ↩