主キーの移行戦略

このドキュメントでは、ホットスポット化の問題を回避するために、データベース テーブルから Spanner に主キーを移行する方法について説明します。ホットスポットとは単一ノードへのオペレーションの集中であり、この現象が発生すると、書き込みが複数の Spanner ノード間で負荷分散されないため、書き込みスループットが 1 つのノードの容量までに制限されます。ホットスポットの最も一般的な原因は、主キーの最初の部分として単調に増加または減少する列(通常のシーケンスやタイムスタンプなど)です。

全体的な戦略

Spannerでは、いずれのテーブルでも、複数の行を保存する必要がある場合は、そのテーブルの 1 つ以上の列からなる主キーが必要になります。テーブルの主キーはテーブル内の各行を一意に識別し、Spanner は主キーを使用してテーブルの行を並べ替えます。Spanner は高度に分散型であるため、次の手法を使用して一意の主キー値を生成すると、ホットスポットのリスクを軽減できます。

  • Spanner でサポートされている、ホットスポット対応の自動生成キー機能を使用します(詳細については、自動生成されたキーを移行するのセクションをご覧ください)。
    • GENERATE_UUID() 関数(GoogleSQLPostgreSQL)を使用して、STRING(36) データ型の Universally Unique Identifier(UUID バージョン 4)の値を生成します。RFC 4122 では、UUID バージョン 4 形式が定義されています。
    • ビット反転した正のシーケンス(GoogleSQLPostgreSQL)を使用します。このようなシーケンスでは、上位ビットがすでに反転している正の値が生成され、正の 64 ビット数値空間に均等に分散されます。
  • キーの順序を入れ替える。単調に増減する値を含む列がキーの最初の部分にならないようにします。
  • 一意キーのハッシュを作成して、論理シャード間に書き込みを分散する。一意キーのハッシュを含む列を作成し、そのハッシュ列(またはハッシュ列と一意キー列の組み合わせ)を主キーとして使用します。これにより、新しい行がキースペース全体に均等に分散されるため、ホットスポットの作成を回避できます。

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

次に、音楽トラックのデータベース用のテーブルを作成する DDL ステートメントの例を示します。

GoogleSQL

CREATE TABLE Singers (
  SingerId   INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  SingerInfo BYTES(MAX),
  BirthDate  DATE,
) PRIMARY KEY(SingerId);

PostgreSQL

CREATE TABLE Singers (
  SingerId   bigint NOT NULL,
  FirstName  varchar(1024),
  LastName   varchar(1024),
  SingerInfo bytea,
  BirthDate  date,
  PRIMARY KEY(SingerId)
);

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

このセクションでは、ソーステーブルが自動生成されたキー機能をすでに使用している次のシナリオの戦略と例について説明します。

  • UUID 主キーを使用するソーステーブルからの移行。
  • 順次生成される整数キーを使用するソーステーブルからの移行。さまざまなデータベースがサポートするシーケンス、IDENTITY 列(さまざまなデータベースがサポートしている)、PostgreSQL の SERIAL データ型、MySQL AUTO_INCREMENT 列属性が例として挙げられますが、これらに限定されません。
  • ビット反転キーを使用するソーステーブルからの移行。ソース データベースが Cloud Spanner であり、このデータベースでビット反転シーケンス値のガイドを使用してキーの値を作成するとします。

すべての戦略において、Spanner はソース データベースから移行するデータを変更しない点に留意することが重要です。新しいデータを生成するメソッドのみを変更します。

UUID キー列を移行する

ソーステーブルが UUID 列を使用している場合は、列を STRING 型に変換し、RFC 4122 仕様に従って値を小文字にし、GENERATE_UUID() 関数(GoogleSQLPostgreSQL)を列のデフォルト値として使用できます。次に例を示します。

GoogleSQL


CREATE TABLE UserAccessLog (
UserId     STRING(36) DEFAULT (GENERATE_UUID()),
...
) PRIMARY KEY (UserId);

PostgreSQL


CREATE TABLE UserAccessLog (
UserId     varchar(36) DEFAULT SPANNER.GENERATE_UUID(),
...
PRIMARY KEY (UserId)
);

連続したキー列を移行する

ソース データベース システムがキー列の連続値を生成する場合は、Cloud Spanner スキーマ内でビット反転した正のシーケンス(GoogleSQLPostgreSQL)を使用して、64 ビットの正の整数空間に均等に分散する値を生成できます。Spanner シーケンスが移行された値と重複する値を生成しないようにするには、その値に対してスキップされる範囲を定義します。たとえば、ソース データベースが 32 ビット整数のみを生成していることがわかっている場合は、次の 2 つのシーケンスで 1~4,294,967,296(2^32)の範囲をスキップできます。

GoogleSQL


CREATE SEQUENCE MyFirstSequence OPTIONS (
  sequence_kind = "bit_reversed_positive",
  skip_range_min = 1,
  skip_range_max = 4294967296
);

ALTER SEQUENCE MySecondSequence SET OPTIONS (
  skip_range_min = 1,
  skip_range_max = 4294967296
);

PostgreSQL


CREATE SEQUENCE MyFirstSequence BIT_REVERSED_POSITIVE
  SKIP RANGE 1 4294967296;

ALTER SEQUENCE MySecondSequence SKIP RANGE 1 4294967296;

ビット反転キー列を移行する

ソース データベースでのホットスポットの問題を回避するため、すでにキーの値をビット反転している場合は、Cloud Spanner のビット反転した正のシーケンス(GoogleSQLPostgreSQL)を使用して、引き続きそれらの値を生成することもできます。重複値の生成を回避するため、カスタム番号からカウンタを開始するようにシーケンスを構成できます。

たとえば、1 から 1,000 までの数値を反転して主キーの値を生成すると、Spanner のシーケンスは 10,000 より大きい任意の数値からカウンタを開始できます。必要に応じて、大きな数値を選択し、データ移行後にソース データベースで発生した新しい書き込みに対してバッファを残すことができます。次の例では、カウンタは 11,000 から始まります。

GoogleSQL


CREATE SEQUENCE MyFirstSequence OPTIONS (
  sequence_kind = "bit_reversed_positive",
  start_with_counter = 11000
);

ALTER SEQUENCE MySecondSequence SET OPTIONS (
  start_with_counter = 11000
);

PostgreSQL


CREATE SEQUENCE MyFirstSequence BIT_REVERSED_POSITIVE
  START COUNTER 11000;

ALTER SEQUENCE MySecondSequence RESTART COUNTER 11000;