主キーの移行

このドキュメントでは、ソース データベース テーブルから Spanner に主キーを移行する手順について説明します。主キーの移行の概要で説明されている情報を理解している必要があります。

始める前に

  • プライマリキーを Spanner に移行するために必要な権限を取得するには、インスタンスに対する Cloud Spanner データベース管理者roles/spanner.databaseAdmin)IAM ロールを付与するよう管理者に依頼してください。

自動生成された連続するキーを移行する

MySQL の AUTO_INCREMENT、PostgreSQL の SERIAL、SQL Server または Oracle の標準 IDENTITY 型など、単調な連続するキーを使用するデータベースから移行する場合は、次の大まかな移行戦略を検討してください。

  1. Spanner で、整数の主キーを使用してソース データベースからテーブル構造を複製します。
  2. 連続する値を含む Spanner の列ごとに、シーケンスを作成し、GET_NEXT_SEQUENCE_VALUEGoogleSQLPostgreSQL)関数を列のデフォルト値として割り当てます。
  3. 元のキーを持つ既存のデータをソース データベースから Spanner に移行します。Spanner 移行ツールまたは Dataflow テンプレートの使用を検討してください。
  4. 必要に応じて、依存するテーブルに外部キー制約を設定できます。
  5. 新しいデータを挿入する前に、既存のキー値の範囲をスキップするように Spanner シーケンスを調整します。
  6. 新しいデータを挿入して、シーケンスが一意のキーを自動的に生成できるようにします。

移行ワークフローの例

次のコードは、SEQUENCE オブジェクトを使用して Spanner でテーブル構造と関連するシーケンスを定義し、移行先テーブルのデフォルトの主キー値としてオブジェクトを設定します。

GoogleSQL

CREATE SEQUENCE singer_id_sequence OPTIONS (
     SequenceKind = 'bit_reversed_positive'
  );

CREATE TABLE Singers (
     SingerId INT64 DEFAULT
     (GET_NEXT_SEQUENCE_VALUE(SEQUENCE SingerIdSequence)),
     Name STRING(1024),
     Biography STRING(MAX),
  ) PRIMARY KEY (SingerId);

CREATE TABLE Albums (
     AlbumId INT64,
     SingerId INT64,
     AlbumName STRING(1024),
     SongList STRING(MAX),
     CONSTRAINT FK_singer_album
     FOREIGN KEY (SingerId)
       REFERENCES Singers (SingerId)
  ) PRIMARY KEY (AlbumId);

PostgreSQL

CREATE SEQUENCE SingerIdSequence BIT_REVERSED_POSITIVE;

CREATE TABLE Singers (
  SingerId BIGINT DEFAULT nextval('SingerIdSequence') PRIMARY KEY,
  Name VARCHAR(1024) NOT NULL,
  Biography TEXT
);

CREATE TABLE Albums (
  AlbumId BIGINT PRIMARY KEY,
  SingerId BIGINT,
  AlbumName VARCHAR(1024),
  SongList TEXT,
  CONSTRAINT FK_singer_album FOREIGN KEY (SingerId) REFERENCES Singers (SingerId)
);

bit_reversed_positive オプションは、シーケンスによって生成される値が INT64 型で、0 より大きく、連続していないことを示します。

既存の行をソース データベースから Spanner に移行する際、キーは変更されません。

新しい挿入で主キーを指定しない場合、Spanner は GET_NEXT_SEQUENCE_VALUE()GoogleSQL または PostgreSQL)関数を呼び出して新しい値を自動的に取得します。

これらの値は [1, 263] の範囲に均等に分散されるため、既存のキーとの競合が発生する可能性があります。これを防ぐには、ALTER_SEQUENCEGoogleSQL または PostgreSQL)を使用してシーケンスを構成し、既存のキーでカバーされている値の範囲をスキップします。

singers テーブルが PostgreSQL から移行され、その主キー singer_idSERIAL 型であるとします。次の PostgreSQL は、移行元データベースの DDL を示しています。

PostgreSQL

CREATE TABLE Singers (
SingerId SERIAL PRIMARY KEY,
Name varchar(1024),
Biography varchar
);

主キー値は単調に増加します。移行後、Spanner で主キー singer_id の最大値を取得できます。Spanner で次のコードを使用します。

GoogleSQL

SELECT MAX(SingerId) FROM Singers;

PostgreSQL

SELECT MAX(SingerId) FROM Singers;

返された値が 20,000 だったとしましょう。範囲 [1, 21000] をスキップするように Spanner シーケンスを構成できます。余分な 1,000 は、最初の移行後のソース データベースへの書き込みに対応するためのバッファとして機能します。Spanner で生成された新しいキーは、ソース PostgreSQL データベースで生成された主キーの範囲と競合しません。Spanner で次のコードを使用します。

GoogleSQL

ALTER SEQUENCE SingerIdSequence SET OPTIONS (
skip_range_min = 1,
skip_range_max = 21000
);

PostgreSQL

ALTER SEQUENCE SingerIdSequence SKIP RANGE 1 21000;

Spanner とソース データベースを使用する

範囲スキップのコンセプトを使用すると、Spanner またはソース データベースのいずれかが主キーを生成するシナリオをサポートできます。たとえば、移行のカットオーバー時にどちらかの方向にレプリケーションを有効にし、障害復旧を確保するといったことが可能です。

これをサポートするために、両方のデータベースで主キーが生成され、データが両データベース間で同期されます。重複しないキー範囲に主キーを作成するように各データベースを構成できます。ソース データベースの範囲を定義するときに、その範囲をスキップするように Spanner シーケンスを構成できます。

たとえば、音楽トラック アプリケーションの移行後に、PostgreSQL から Spanner にデータを複製して、カットオーバーにかかる時間を短縮します。

Spanner でアプリケーションを更新してテストしたら、ソースの PostgreSQL データベースの使用を停止し、Spanner を使用して更新と新しい主キーの記録システムにすることができます。Spanner が引き継いだら、データベース間のデータフローを PostgreSQL インスタンスに逆転できます。

ソース PostgreSQL データベースが SERIAL 主キー(32 ビットの符号付き整数)を使用しているとします。Spanner の主キーはそれより大きい 64 ビットの数値です。PostgreSQL で、主キー列を 64 ビット列または bigint に変更します。ソース PostgreSQL データベースで次のコードを使用します。

PostgreSQL

ALTER TABLE Singers ALTER COLUMN SingerId TYPE bigint;

ソース PostgreSQL データベースのテーブルに CHECK 制約を設定し、SingerId 主キーの値が常に 231-1 以下になるようにします。ソース PostgreSQL データベースで次のコードを使用します。

PostgreSQL

ALTER TABLE Singers ADD CHECK (SingerId <= 2147483647);

Spanner では、[1, 231-1] 範囲をスキップするようにシーケンスを変更できます。Spanner で次のコードを使用します。

GoogleSQL

ALTER SEQUENCE SingerIdSequence SET OPTIONS (
skip_range_min = 1,
skip_range_max = 2147483647 -- 231-1
);

PostgreSQL

ALTER SEQUENCE SingerIdSequence SKIP RANGE 1 2147483648;

ソース PostgreSQL データベースは常に 32 ビットの整数空間にキーを生成しますが、Spanner キーは 64 ビットの整数空間に制限され、すべての 32 ビットの整数値より大きくなります。これにより、両方のデータベースが競合しない主キーを独立して生成できます。

UUID キー列を移行する

UUIDv4 キーは、生成される場所に関係なくほぼ一意になります。他の場所で生成された UUID キーは、Spanner で生成された新しい UUID キーと統合されます。

UUID キーを Spanner に移行するための大まかな戦略は次のとおりです。

  1. デフォルトの式を含む文字列列を使用して、Spanner で UUID キーを定義します。GENERATE_UUID() 関数(GoogleSQLPostgreSQL)を使用します。
  2. ソースシステムからデータをエクスポートして、UUID キーを文字列としてシリアル化します。
  3. 主キーを Spanner にインポートします。
  4. 省略可: 外部キーを有効にします。

移行ワークフローの例を次に示します。

Spanner で、UUID 主キーの列を STRING 型または TEXT 型として定義し、そのデフォルト値として GENERATE_UUID()GoogleSQL または PostgreSQL)を割り当てます。ソース データベースから Spanner にすべてのデータを移行します。移行後、新しい行が挿入されると、Spanner は GENERATE_UUID() を呼び出して主キーの新しい UUID 値を生成します。たとえば、テーブル FanClubs に新しい行が挿入されると、主キー FanClubId は UUIDv4 値を取得します。Spanner で次のコードを使用します。

GoogleSQL

CREATE TABLE Fanclubs (
FanClubId STRING(36) DEFAULT (GENERATE_UUID()),
ClubName STRING(1024),
) PRIMARY KEY (FanClubId);

INSERT INTO FanClubs (ClubName) VALUES ("SwiftFanClub");

PostgreSQL

CREATE TABLE FanClubs (
  FanClubId TEXT DEFAULT spanner.generate_uuid() PRIMARY KEY,
  ClubName VARCHAR(1024)
);

INSERT INTO FanClubs (ClubName) VALUES ('SwiftFanClub');

独自の主キーを移行する

アプリケーションによっては、データの更新頻度を判断したり、新しく作成されたデータをシーケンス化したりするために、主キーの順序を参照する場合があります。外部で生成された連続するキーを Spanner で使用するには、ハッシュなどの均等に分散された値を最初のコンポーネントとして、連続するキーを 2 番目のコンポーネントとして組み合わせる複合キーを作成します。これにより、大規模なホットスポットを作成することなく、連続するキー値を保持できます。次の移行ワークフローを検討してください。

AUTO_INCREMENT 主キーを使用した MySQL テーブルである students を Spanner に移行する必要がある場合を考えてみましょう。ソース MySQL データベースで次のコードを使用します。

MySQL

CREATE TABLE Students (
StudentId INT NOT NULL AUTO_INCREMENT,
Info VARCHAR(2048),
PRIMARY KEY (StudentId)
);

Spanner では、StudentId 列のハッシュを作成することで、生成列 StudentIdHash を追加できます。次に例を示します。

  StudentIdHash = FARM_FINGERPRINT(CAST(StudentId AS STRING))

Spanner で、次のコードを使用します。

GoogleSQL

CREATE TABLE student (
  StudentIdHash INT64 AS (FARM_FINGERPRINT(cast(StudentId as string))) STORED,
  StudentId INT64 NOT NULL,
  Info STRING(2048),
) PRIMARY KEY(StudentIdHash, StudentId);

PostgreSQL

CREATE TABLE Student (
  StudentIdHash bigint GENERATED ALWAYS AS
  (FARM_FINGERPRINT(cast(StudentId AS varchar))) STORED,
  StudentId bigint NOT NULL,
  Info varchar(2048),
  PRIMARY KEY (StudentIdHash, StudentId)
);

次のステップ