PostgreSQL から Spanner(PostgreSQL 言語)に移行する

このページでは、オープンソースの PostgreSQL データベース(以降、PostgreSQL と称します)を Spanner PostgreSQL 言語データベース(以降、Spanner と称します)に移行する方法について説明します。

Spanner と GoogleSQL 言語への移行については、PostgreSQL から Spanner(GoogleSQL 言語)への移行をご覧ください。

移行の制約

Spanner では、特定のコンセプトの扱いが他のエンタープライズ データベース管理ツールとは異なるため、その機能を最大限に活用するにはアプリケーションのアーキテクチャを調整する必要がある場合があります。ニーズを満たすために、Spanner を Google Cloud の他のサービスで補完する必要がある場合もあります。

ストアド プロシージャとトリガー

Spanner はデータベース レベルでのユーザーコードの実行をサポートしていないため、移行の一環として、データベースのストアド プロシージャとトリガーによって実装されたビジネス ロジックをアプリケーションに移す必要があります。

シーケンス

Spanner では、主キーの値を生成するデフォルトの方法として、UUID バージョン 4 を使用することをおすすめします。GENERATE_UUID() 関数(GoogleSQLPostgreSQL)は、STRING 型で表される UUID バージョン 4 の値を返します。

整数値を生成する必要がある場合は、Spanner でビット反転した正のシーケンス(GoogleSQLPostgreSQL)がサポートされ、これにより 64 ビットの正の数空間に均等に分散する値が生成されます。ホットスポットの問題を回避するため、これらの数値を使用できます。

詳細については、主キーのデフォルト値の戦略をご覧ください。

アクセス制御

Spanner は、テーブルレベルと列レベルでのきめ細かいアクセス制御をサポートしています。ビューに対するきめ細かいアクセス制御はサポートされていません。詳細については、きめ細かいアクセス制御についてをご覧ください。

移行プロセス

移行には次のタスクが含まれます。

  • PostgreSQL スキーマを Spanner にマッピングする。
  • SQL クエリを変換する。
  • Spanner のインスタンス、データベース、スキーマを作成する。
  • アプリケーションをリファクタリングして Spanner データベースと連携できるようにする。
  • データを移行する。
  • 新しいシステムを検証して本番稼働状態に移行する。

ステップ 1: PostgreSQL スキーマを Spanner にマッピングする

データベースをオープンソース PostgreSQL から Spanner に移行するための最初のステップは、どのようなスキーマ変更が必要かを判断することです。

主キー

Spannerでは、いずれのテーブルでも、複数の行を保存する必要がある場合は、そのテーブルの 1 つ以上の列からなる主キーが必要になります。テーブルの主キーはテーブル内の各行を一意に識別し、Spanner は主キーを使用してテーブルの行を並べ替えます。Spanner は高度に分散型であるため、データの増加に合わせてスケーリングされる主キーを生成する方法を選択することが重要です。詳細については、推奨の主キーの移行戦略をご覧ください。

主キーを指定した後は、後でテーブルを削除して再作成しない限り、主キー列を追加または削除したり、主キーの値を変更したりすることはできません。主キーの指定方法の詳細については、スキーマとデータモデルの主キーをご覧ください。

インデックス

PostgreSQL の B-tree インデックスは、Spanner のセカンダリ インデックスと似ています。Spanner データベースでは、パフォーマンスを向上させる目的でよく検索される列にインデックスを付けたり、テーブルで指定された UNIQUE 制約を代用するためにセカンダリ インデックスを使用します。たとえば、PostgreSQL DDL に次のステートメントがあるとします。

 CREATE TABLE customer (
    id CHAR (5) PRIMARY KEY,
    first_name VARCHAR (50),
    last_name VARCHAR (50),
    email VARCHAR (50) UNIQUE
 );

この場合、Spanner DDL では次のようなステートメントを使用します。

CREATE TABLE customer (
   id VARCHAR(5) PRIMARY KEY,
   first_name VARCHAR(50),
   last_name VARCHAR(50),
   email VARCHAR(50)
   );

CREATE UNIQUE INDEX customer_emails ON customer(email);

任意の PostgreSQL テーブルのインデックスを見つけるには、psql\di メタコマンドを実行します。

必要なインデックスが判明したら、CREATE INDEX ステートメントを追加してそれらを作成します。セカンダリ インデックスのガイダンスに従ってください。

Spanner はインデックスをテーブルとして実装するので、単調増加する列(TIMESTAMP データを含む列など)にインデックスを付けると、ホットスポットが発生することがあります。ホットスポットを回避する方法の詳細については、DBA が Spanner について知っておくべきこと、パート 1: キーとインデックスをご覧ください。

Spanner ではテーブルと同じ方法でセカンダリ インデックスが実装されるため、インデックス キーとして使用される列値にはテーブルの主キーと同じ制約が適用されます。したがって、Spanner のテーブルと同様にインデックスの整合性も保証されます。

セカンダリ インデックスを使用した値検索は、テーブル結合を使用したクエリと実質的に同じです。INCLUDE 句を使用して元のテーブルの列値のコピーをセカンダリ インデックスに保存して Covering index とすることで、インデックスを使用したクエリのパフォーマンスが向上します。

Spanner のクエリ オプティマイザでは、クエリの対象となるすべての列がセカンダリ インデックス自体に保存されている場合(カバードクエリ)に限り、セカンダリ インデックスが自動的に使用されます。元のテーブルの列をクエリするときにインデックスを強制的に使用するには、次の例に示すように、SQL ステートメントで FORCE INDEX ディレクティブを使用する必要があります。

SELECT *
FROM MyTable /*@ FORCE_INDEX=MyTableIndex */
WHERE IndexedColumn=$1;

次に、Albums テーブルにセカンダリ インデックスを作成する DDL ステートメントの例を示します。

CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle);

データの読み込み後に追加のインデックスを作成すると、インデックスへのデータの入力に時間がかかることがあります。追加する頻度を 1 日平均 3 回に制限することをおすすめします。セカンダリ インデックスの作成方法の詳細については、セカンダリ インデックスをご覧ください。インデックス作成の制限事項の詳細については、スキーマの更新をご覧ください。

ビュー

Spanner ビューは読み取り専用です。データの挿入、更新、削除には使用できません。詳細については、ビューをご覧ください。

生成列

Spanner は生成された列をサポートします。構文の違いと制限については、生成された列の作成と管理をご覧ください。

テーブル インターリーブ

Spanner には、1 対多の親子関係を持つ 2 つのテーブルを定義できる機能があります。この機能を使用すると、ストレージ内でデータ行の親子がインターリーブされます。これにより、テーブルが効果的に事前結合され、親と子をともに対象とするクエリでより効率的なデータ取得が可能になります。

子テーブルの主キーは、親テーブルの主キー列で始まる必要があります。子行の観点からは、親行の主キーは外部キーとなります。最大 6 レベルの親子関係を定義できます。

子テーブルに対してON DELETEアクションを定義すると、親行が削除された場合の動作を指定できます。子行をすべて削除するか、子行が存在する間は親行の削除をブロックするかのいずれかの動作を選択できます。

次に、Albums テーブルを作成して、前に定義した親の Singers テーブルにインターリーブする例を示します。

CREATE TABLE Albums (
 SingerID      bigint,
 AlbumID       bigint,
 AlbumTitle    varchar,
 PRIMARY KEY (SingerID, AlbumID)
 )
 INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

詳細については、インターリーブされたテーブルの作成をご覧ください。

データの種類

次の表に、Spanner 用の PostgreSQL Interface でサポートされていないオープンソースの PostgreSQL データ型を示します。

データ型 代わりに使用
bigserial、serial8 bigint, int8
bit [ (n) ] -
ビット可変 [ (n) ], varbit [ (n) ] -
box -
文字 [ (n) ], char [ (n) ] 文字の変更
cidr text
-
inet text
整数、int64 bigint, int8
interval [fields] [ (p) ] bigint
json jsonb
-
lseg -
macaddr text
money 数値、10 進数
パス -
pg_lsn -
point -
polygon -
realfloat4 double precision, float8
smallint, int2 bigint, int8
smallserial, serial2 bigint, int8
serial, serial4 bigint, int8
time [ (p) ] [ without time zone ] HH:MM:SS.sss 表記を使用した。
time [ (p) ] とタイムゾーン HH:MM:SS.sss+ZZZZ 表記を使用した。または 2 列を使用します。
timestamp [ (p) ] [ without time zone ] テキストまたは timestamptz
tsquery -
tsvector -
txid_snapshot -
uuid テキストまたはバイト
xml text

ステップ 2: SQL クエリを変換する

Spanner には、変換の負担の軽減に役立つオープンソースの PostgreSQL 関数が多数あります。

SQL クエリを実行する際には、Google Cloud コンソールの [Spanner Studio] ページを使用してクエリのプロファイリングを行うことができます。一般に、大きなテーブルを対象としてテーブル全体のスキャンを実行するクエリには非常にコストがかかるため、使用は控えめにする必要があります。SQL クエリの最適化の詳細については、SQL のベスト プラクティスのドキュメントをご覧ください。

ステップ 3: Spanner のインスタンス、データベース、スキーマを作成する

PostgreSQL 言語でインスタンスを作成し、データベースを作成します。次に、PostgreSQL のデータ定義言語(DDL)を使用してスキーマを作成します。

pg_dump を使用して、PostgreSQL データベース内のオブジェクトを定義する DDL ステートメントを作成し、前のセクションの説明に従ってステートメントを変更します。DDL ステートメントを更新したら、DDL ステートメントを使用して Spanner インスタンスにデータベースを作成します。

詳しくは以下をご覧ください。

ステップ 4: アプリケーションをリファクタリングする

変更されたスキーマと変更された SQL クエリを考慮し、プロシージャやトリガーなどのデータベース常駐ロジックを置き換えるアプリケーション ロジックを追加します。

ステップ 5: データを移行する

データを移行するには、次の 2 つの方法があります。

  • Harbourbridge を使用する。

    Harbourbridge は、スキーマとデータの移行の両方をサポートしています。pg_dump ファイルまたは CSV ファイルをインポートするか、オープンソースの PostgreSQL データベースに直接接続してインポートできます。

  • COPY FROM STDIN コマンドを使用する。

    詳細については、データをインポートする COPY コマンドをご覧ください。