DynamoDB から Spanner への移行

このチュートリアルでは、Amazon DynamoDB から Spanner に移行する方法を説明します。このチュートリアルは主に、NoSQL システムから Spanner への移行を考えているアプリ所有者を対象としています。Spanner は完全にリレーショナルでフォールト トレラント、かつ拡張性の高い SQL データベース システムであり、トランザクションをサポートしています。Amazon DynamoDB テーブルの使用方法がタイプとレイアウトの面で一貫していれば、Spanner へのマッピングは容易です。Amazon DynamoDB のテーブルに含まれているデータ型や値が限定されていない場合は、DatastoreFirebase など他の NoSQL サービスに移行するほうが簡単かもしれません。

このチュートリアルでは、データベース スキーマ、データ型、NoSQL の基本、リレーショナル データベース システムについての知識があることを前提としています。このチュートリアルでは主に、定義済みのタスクを実行して移行を行ってみます。チュートリアルの終了後は、提示されたコードや手順を環境に合わせて変更できます。

次のアーキテクチャ図は、このチュートリアルでデータの移行に使用するコンポーネントの概要を示しています。

移行コンポーネントのアーキテクチャ図

目標

  • Amazon DynamoDB から Spanner にデータを移行する。
  • Spanner データベースおよび移行テーブルを作成する。
  • NoSQL スキーマをリレーショナル スキーマにマッピングする。
  • Amazon DynamoDB を使用するサンプル データセットを作成しエクスポートする。
  • Amazon S3 と Cloud Storage の間でデータを転送する。
  • Dataflow を使用して Spanner にデータをロードする。

費用

このチュートリアルでは、以下の課金対象の Google Cloud Platform コンポーネントを使用します。

  • GKE
  • Pub / Sub
  • Cloud Storage
  • Dataflow

Spanner の請求料金は、毎月の請求期間のノード時間数と、格納されたデータの量に基づいて決定されます。このチュートリアルでは、これらのリソースを最小限の構成で利用し、最後にクリーンアップします。実際のシナリオでは、スループットとストレージの要件を予測し、Spanner インスタンスのドキュメントを使用して、必要なノード数を判定します。

このチュートリアルでは、Google Cloud のリソースに加えて、次の Amazon Web Services(AWS)リソースを使用します。

  • Amazon EMR
  • AWS Lambda
  • Amazon S3
  • Amazon DynamoDB

これらのサービスは移行プロセスにのみ必要になります。チュートリアルの最後には、指示に従ってすべてのリソースをクリーンアップし、不要な料金が発生しないようにしてください。こうした料金の見積もりには、AWS 料金計算ツールを利用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを出すことができます。 GCP を初めてご利用の場合は、無料トライアルをご利用いただけます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. GCP Console のプロジェクト セレクタのページで、GCP プロジェクトを選択または作成します。

    プロジェクト セレクタのページに移動

  3. Google Cloud Platform プロジェクトに対して課金が有効になっていることを確認します。 プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  4. Spanner、Pub / Sub、Compute Engine、および Dataflow必要な を有効にします。

    を有効にする

このチュートリアルを終了した後、作成したリソースを削除すると、それ以上の請求は発生しません。詳しくは、クリーンアップをご覧ください。

環境の準備

このチュートリアルでは、Cloud Shell でコマンドを実行します。Cloud Shell では Google Cloud のコマンドラインにアクセスできるほか、Google Cloud で開発を行うために必要な Cloud SDK などのツールも利用できます。Cloud Shell の初期化には数分かかることがあります。

  1. Cloud Shell をアクティブにします。

    Cloud Shell をアクティブにする

  2. デフォルトの Compute Engine ゾーンを設定します。例: us-central1-b

    gcloud config set compute/zone us-central1-b
    
  3. サンプルコードが格納された GitHub リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/dynamodb-spanner-migration.git
    
  4. クローンされたディレクトリに移動します。

    cd dynamodb-spanner-migration
    
  5. Python 仮想環境を作成します。

    virtualenv --python python2 env
    
  6. 仮想環境をアクティブにします。

    source env/bin/activate
    
  7. 必要な Python モジュールをインストールします。

    pip install -r requirements.txt
    

AWS アクセスの構成

このチュートリアルでは、Amazon DynamoDB テーブル、Amazon S3 バケット、その他のリソースを作成して削除します。これらのリソースにアクセスするには、まず必要な AWS Identity and Access Management(IAM)権限を作成する必要があります。テスト アカウントまたはサンドボックス AWS アカウントを使用すれば、同じアカウントで本番環境のリソースに影響を及ぼす心配もありません。

AWS Lambda の AWS IAM 役割を作成する

このセクションでは、チュートリアル後半のステップで AWS Lambda が使用する AWS IAM 役割を作成します。

  1. AWS console で、[IAM] セクションに移動し、[Roles] をクリックしてから、[Create role] を選択します。
  2. [Choose the service that will use this role] で [Lambda] をクリックし、[Next: Permissions] を選択します。
  3. [Policy Type] ボックスに「AWSLambdaDynamoDBExecutionRole」と入力します。
  4. [AWSLambdaDynamoDBExecutionRole] チェックボックスをオンにして、[Next: Review] をクリックします。
  5. [Role name] ボックスに「dynamodb-spanner-lambda-role」と入力し、[Create role] をクリックします。

AWS IAM ユーザーを作成する

次の手順に従って、AWS リソースにプログラムでアクセスできる AWS IAM ユーザーを作成します。AWS リソースはこのチュートリアルを通して使用します。

  1. AWS コンソールの [IAM] セクションから、[Users] をクリックし、[Add User] を選択します。
  2. [User name] ボックスに「dynamodb-spanner-migration」と入力します。
  3. [Access type] で、[Programmatic access] をクリックします。

  4. [Next: Permissions] をクリックします。

  5. [Attach existing policies directly] をクリックし、次の 2 つのポリシーを選択します。

    • AmazonDynamoDBFullAccesswithDataPipeline
    • AmazonS3FullAccess
  6. [Next: Review] をクリックし、[Create user] をクリックします。

  7. [Show] をクリックして、認証情報を表示します。新しく作成されたユーザーのアクセスキー ID とシークレット アクセスキーが表示されます。次のセクションで認証情報が必要になるため、このウィンドウは開いたままにしておきます。これらの認証情報は安全な場所に保管してください。この情報を使用すればアカウントを改ざんし、環境に影響を与えることが可能になるためです。このチュートリアルの最後に、IAM ユーザーを削除できます。

AWS コマンドライン インターフェースを構成する

  1. Cloud Shell で、AWS コマンドライン インターフェース(CLI)を構成します。

    aws configure
    

    次の出力が表示されます。

    $ aws configure
    AWS Access Key ID [None]: PASTE_YOUR_ACCESS_KEY_ID
    AWS Secret Access Key [None]: PASTE_YOUR_SECRET_ACCESS_KEY
    Default region name [None]: us-west-2
    Default output format [None]:
    user@project:~/dynamodb-spanner$
    
    • 作成した AWS IAM アカウントから、ACCESS KEY IDSECRET ACCESS KEY を入力します。
    • [Default region name] フィールドに「us-west-2」と入力します。他のフィールドはデフォルト値のままにしておきます。
  2. AWS IAM コンソール ウィンドウを閉じます。

データモデルの理解

次のセクションでは、Amazon DynamoDB と Spanner のデータ型、キー、インデックスの類似点および相違点を概説します。

データ型

Spanner は標準の SQL データ型を使用します。次の表は、Amazon DynamoDB のデータ型がどのように Spanner のデータ型にマッピングされるかを示しています。

Amazon DynamoDB Spanner
数値 精度または使用目的に応じて、INT64、FLOAT64、TIMESTAMP、または DATE にマッピングされます。
文字列 文字列
ブール値 BOOL
Null 明示的な型はありません。列には NULL 値を入れることができます。
2 項 バイト
セット 配列
マップとリスト 構造が一貫しており、テーブルの DDL 構文を使用して記述できる場合は構造体。

主キー

Amazon DynamoDB の主キーは一意性を確立するもので、ハッシュキーか、ハッシュキーと範囲キーの組み合わせのいずれかになります。このチュートリアルはまず、主キーがハッシュキーである Amazon DynamoDB テーブルの移行をモデル化することから始まります。このハッシュキーは Spanner テーブルの主キーになります。後で記載するインターリーブされたテーブルのセクションでは、Amazon DynamoDB テーブルが、ハッシュキーと範囲キーで構成される主キーを使用する状況をモデリングします。

セカンダリ インデックス

Amazon DynamoDB と Spanner はいずれも、非主キー属性でのインデックスの作成をサポートしています。Amazon DynamoDB テーブルのセカンダリ インデックスをメモしておきます。このセクションの後半で Spanner テーブルでインデックスを作成する際に使用します。

サンプル テーブル

このチュートリアルを効率よく進めるために、次のサンプル テーブルを Amazon DynamoDB から Spanner に移行します。

Amazon DynamoDB Spanner
テーブル名 Migration Migration
主キー "Username" : String "Username" : STRING(1024)
キーのタイプ ハッシュ なし
その他のフィールド Zipcode: Number Subscribed: Boolean ReminderDate: String PointsEarned: Number Zipcode: INT64 Subscribed: BOOL ReminderDate: DATE PointsEarned: INT64

Amazon DynamoDB テーブルの準備

次のセクションでは、Amazon DynamoDB のソーステーブルを作成し、そこにデータを追加します。

  1. Cloud Shell で、サンプル テーブルの属性を使用する Amazon DynamoDB テーブルを作成します。

    aws dynamodb create-table --table-name Migration \
        --attribute-definitions AttributeName=Username,AttributeType=S \
        --key-schema AttributeName=Username,KeyType=HASH \
        --provisioned-throughput ReadCapacityUnits=75,WriteCapacityUnits=75
    
  2. テーブルのステータスが ACTIVE であることを確認します。

    aws dynamodb describe-table --table-name Migration \
        --query 'Table.TableStatus'
    
  3. テーブルにサンプルデータを追加します。

    python make-fake-data.py --table Migration --items 25000
    

Spanner データベースの作成

単一ノードのインスタンスを作成します。これはテストとこのチュートリアルの内容に適しています。本番環境の場合は、Spanner インスタンスのドキュメントを参照して、データベースのパフォーマンス要件を満たす適切なノード数を決定してください。

この例では、データベースと同時にテーブル スキーマを作成します。データベースを作成した後にスキーマを更新することも可能で、よく行われます。

  1. デフォルトの Compute Engine ゾーンを設定した同じリージョンに Spanner インスタンスを作成します。例: us-central1

    gcloud spanner instances create spanner-migration \
        --config=regional-us-central1 --nodes=1 \
        --description="Migration Demo"
    
  2. Spanner インスタンスにサンプル テーブルとともにデータベースを作成します。

    gcloud spanner databases create migrationdb \
        --instance=spanner-migration \
        --ddl "CREATE TABLE Migration ( \
                Username STRING(1024) NOT NULL, \
                PointsEarned INT64, \
                ReminderDate DATE, \
                Subscribed BOOL, \
                Zipcode INT64, \
             ) PRIMARY KEY (Username)"
    

データベースの一時停止

次のセクションでは、Amazon DynamoDB ソーステーブルをエクスポートし、Pub/Sub レプリケーションを設定して、エクスポート中に発生したデータベースへの変更を取得する方法について説明します。データベースへの変更がべき等ではなく、同じデータを複数回書き込むことが安全でない場合は、テーブルに対するアプリの変更を一時停止できるメンテナンス期間中に、次の手順を行うことが妥当です。

変更を Pub/Sub にストリーミングする

データベースの変更を Pub/Sub にストリーミングするには、AWS Lambda 関数を使用します。

  1. Cloud Shell で、ソーステーブルでの Amazon DynamoDB ストリームを有効にします。

    aws dynamodb update-table --table-name Migration \
        --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
    
  2. 変更を受け取るための Pub/Sub トピックを設定します。

    gcloud pubsub topics create spanner-migration
    

    次の出力が表示されます。

    $ gcloud pubsub topics create spanner-migration
    Created topic [projects/your-project/topics/spanner-migration].
    
  3. IAM サービス アカウントを作成して、テーブルの更新を Cloud Pub/Sub トピックに push します。

    gcloud iam service-accounts create spanner-migration \
        --display-name="Spanner Migration"
    

    次の出力が表示されます。

    $ gcloud iam service-accounts create spanner-migration --display-name="Spanner Migration"
    Created service account [spanner-migration].
    
  4. サービス アカウントが Pub/Sub に公開する権限を持つように、Cloud IAM ポリシー バインディングを作成します。GOOGLE_CLOUD_PROJECT は、Google Cloud プロジェクトの名前で置き換えます。

    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --role roles/pubsub.publisher \
        --member serviceAccount:spanner-migration@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    

    次の出力が表示されます。

    $ gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
      --role roles/pubsub.publisher \
      --member serviceAccount:spanner-migration@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    bindings: (...truncated...) - members: - serviceAccount:spanner-migration@solution-z.iam.gserviceaccount.com role: roles/pubsub.publisher
  5. サービス アカウントの認証情報を作成します。

    gcloud iam service-accounts keys create credentials.json \
        --iam-account spanner-migration@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    

    次の出力が表示されます。

    $ gcloud iam service-accounts keys create credentials.json --iam-account spanner-migration@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    created key [5e559d9f6bd8293da31b472d85a233a3fd9b381c] of type [json] as [credentials.json] for [spanner-migration@your-project.iam.gserviceaccount.com]
  6. AWS Lambda 関数を準備しパッケージ化して、Amazon DynamoDB テーブルの変更を Pub/Sub トピックに push します。

    pip install --ignore-installed --target=lambda-deps google-cloud-pubsub==0.35
    cd lambda-deps; zip -r9 ../pubsub-lambda.zip *; cd -
    zip -g pubsub-lambda.zip ddbpubsub.py
  7. 前に作成した Lambda 実行の役割の Amazon Resource Name(ARN)を取得する変数を作成します。

    LAMBDA_ROLE=$(aws iam list-roles \
        --query 'Roles[?RoleName==`dynamodb-spanner-lambda-role`].[Arn]' \
        --output text)
    
  8. pubsub-lambda.zip パッケージを使用して、AWS Lambda 関数を作成します。

    aws lambda create-function --function-name dynamodb-spanner-lambda \
        --runtime python2.7 --role $LAMBDA_ROLE \
        --handler ddbpubsub.lambda_handler --zip fileb://pubsub-lambda.zip \
        --environment Variables="{SVCACCT=$(base64 -w 0 credentials.json),PROJECT=$GOOGLE_CLOUD_PROJECT,TOPIC=spanner-migration}"
    

    次の出力が表示されます。

    $ aws lambda create-function --function-name dynamodb-spanner-lambda \
    >   --runtime python2.7 --role $LAMBDA_ROLE \
    >   --handler ddbpubsub.lambda_handler --zip fileb://pubsub-lambda.zip \
    >   --environment Variables="{SVCACCT=$(base64 -w 0 credentials.json),PROJECT=$GOOGLE_CLOUD_PROJECT,TOPIC=spanner-migration}"
    {
        "FunctionName": "dynamodb-spanner-lambda",
        "LastModified": "2018-07-07T12:53:58.670+0000",
        "RevisionId": "e58e8408-cd3a-4155-a184-4efc0da80bfb",
        "MemorySize": 128,
    ... truncated output...
  9. テーブルに Amazon DynamoDB ストリームの ARN を取得する変数を作成します。

    STREAMARN=$(aws dynamodb describe-table \
        --table-name Migration \
        --query "Table.LatestStreamArn" \
        --output text)
    
  10. Lambda 関数を Amazon DynamoDB テーブルに関連付けます。

    aws lambda create-event-source-mapping --event-source $STREAMARN \
        --function-name dynamodb-spanner-lambda --enabled \
        --starting-position TRIM_HORIZON
    
  11. テスト中の応答性を最適化するには、前のコマンドの末尾に --batch-size 1 を追加します。こうするとアイテムを作成、更新、または削除するたびに関数がトリガーされます。

    次の出力が表示されます。

    $ aws lambda create-event-source-mapping --event-source $STREAMARN \
    >     --function-name dynamodb-spanner-lambda --enabled --starting-position TRIM_HORIZON
    {
        "UUID": "44e4c2bf-493a-4ba2-9859-cde0ae5c5e92",
        "StateTransitionReason": "User action",
        "LastModified": 1530662205.549,
        "BatchSize": 100,
        "EventSourceArn": "arn:aws:dynamodb:us-west-2:accountid:table/Migration/stream/2018-07-03T15:09:57.725",
        "FunctionArn": "arn:aws:lambda:us-west-2:accountid:function:dynamodb-spanner-lambda",
        "State": "Creating",
        "LastProcessingResult": "No records processed"
    }
    

Amazon DynamoDB テーブルを Amazon S3 にエクスポートする

  1. Cloud Shell で、以降の複数のセクションで使用するバケット名の変数を作成します。

    BUCKET=$DEVSHELL_PROJECT_ID-dynamodb-spanner-export
    
  2. DynamoDB エクスポートを受け取る Amazon S3 バケットを作成します。

    aws s3 mb s3://$BUCKET
    
  3. AWS Management Console で、[Data Pipeline] をクリックします。

  4. [Create new pipeline] をクリックして、エクスポート ジョブを定義します。

  5. [Name] フィールドに、「Export to Amazon S3」と入力します。

  6. [Source] で、次の項目を選択します。

    • [Build using a template]。
    • [Export DynamoDB table to Amazon S3]。
  7. [Parameters] セクションで、次の項目を定義します。

    1. [Source DynamoDB table name] フィールドに、「Migration」と入力します。
    2. [Output S3 folder] フィールドで、[Folder] アイコンをクリックして、作成した [Your-Project-ID]-dynamodb-spanner-export Amazon S3 バケットを選択します。ここで、[YOUR-PROJECT-ID] は Google Cloud プロジェクト ID を表します。
    3. エクスポートの際に使用可能な読み取り容量をすべて使用するには、[DynamoDB read throughput ratio] フィールドに「1」と入力します。本番環境ではこの値を調整して、稼働中のオペレーションを妨げないようにします。
    4. [Region of your DynamoDB table] フィールドに、us-west-2 などのリージョン名を入力します。
  8. すぐにバックアップ ジョブを開始する場合は、[Run] の [Schedule] セクションで、[On pipeline activation] をクリックします。

  9. [Pipeline Configuration] で、[Logging] フィールドに Disabled と入力します。本番用のテーブルをこのガイドに従って移行する場合は、このオプションを有効にし、別の Amazon S3 バケットを指すようにしておきます。そうすることで、エラーのトラブルシューティングに役立つログを生成できます。他のデフォルト パラメータはそのままにしておいてください。

  10. バックアップ処理を開始するには、[Activate] をクリックします。

  11. 検証の警告に対処するよう求められたら、[Activate] をクリックします。本番環境では、ジョブに最大の時間を設定してロギングを有効にします。

  12. [Refresh] をクリックして、バックアップ プロセスのステータスを更新します。リソースを作成してエクスポートを完了するジョブには数分かかります。本番環境では、データ パイプライン ジョブを変更してより多くの EMR リソースを使用することで、このプロセスを高速化できます。

    プロセスが終了したら、出力バケットを確認します。

    aws s3 ls --recursive s3://$BUCKET
    

    _SUCCESS という名前のファイルがあれば、エクスポート ジョブは完了しています。

    $ aws s3 ls --recursive s3://$BUCKET
    
    2018-06-30 13:08:11 3736518 2018-06-30-20-01-21/76b53eea-46d1-4293-ba51-11759f5c65fa
    2018-06-30 13:08:20       0 2018-06-30-20-01-21/_SUCCESS
    2018-06-30 13:08:20     178 2018-06-30-20-01-21/manifest
    

データベースを開く

エクスポートの前にデータベースへの書き込みを一時停止した場合は、ここでデータベースへの書き込みを再開します。Pub/Sub 配信が開始されたため、エクスポート後に発生したテーブルの変更をすべて push できます。

エクスポートしたテーブルを Cloud Storage にコピーする

  1. Cloud Shell で、Amazon S3 からエクスポートされたファイルを受信する Cloud Storage バケットを作成します。

    gsutil mb gs://$BUCKET
    
  2. Amazon S3 から受信したファイルを Cloud Storage に同期します。ほとんどのコピー オペレーションには、rsync コマンドが有効です。エクスポート ファイルが大きい(数 GB 以上の)場合は、Cloud Storage Transfer Service を使用して、バックグラウンド転送を管理します。

    gsutil rsync -d -r s3://$BUCKET gs://$BUCKET
    

    次の出力が表示されます。

    $ gsutil rsync -d -r s3://$BUCKET gs://$BUCKET
    Building synchronization state...
    Starting synchronization...
    Copying s3://project-dynamodb-spanner-export/2018-06-30-20-01-21/76b53eea-46d1-4293-ba51-11759f5c65fa [Content-Type=binary/octet-stream]...
    Copying s3://project-dynamodb-spanner-export/2018-06-30-20-01-21/_SUCCESS [Content-Type=binary/octet-stream]...
    Copying s3://project-dynamodb-spanner-export/2018-06-30-20-01-21/manifest [Content-Type=binary/octet-stream]...
    / [3 files][  3.6 MiB/  3.6 MiB]
    Operation completed over 3 objects/3.6 MiB.
    

データを一括インポートする

  1. エクスポートされたファイルのデータを Spanner テーブルに書き込むには、サンプルの Apache Beam コードを使用して Dataflow ジョブを実行します。

    cd dataflow
    mvn compile
    mvn exec:java \
        -Dexec.mainClass=com.example.spanner_migration.SpannerBulkWrite \
        -Dexec.args="--project=$GOOGLE_CLOUD_PROJECT \
                     --instanceId=spanner-migration \
                     --databaseId=migrationdb \
                     --table=Migration \
                     --importBucket=$BUCKET \
                     --runner=dataflow"
    
    1. Cloud Console でインポート ジョブの進行状況を確認するには、Dataflow に移動します。

      Dataflow に移動

    2. ジョブの実行中、実行グラフを表示してログを検討できます。[Status] が [Running] と表示されているジョブをクリックします。

      インポート ジョブの実行

  2. 各ステージをクリックすると、処理済みの要素の数が表示されます。すべてのステージが [Succeeded] になると、インポートは完了です。Amazon DynamoDB テーブルで作成された要素と同じ数の要素が、各ステージで処理済みと表示されます。

    インポート ジョブの成功のステージ

  3. 宛先 Spanner テーブルのレコード数が Amazon DynamoDB テーブルの項目数と一致していることを確認します。

    aws dynamodb describe-table --table-name Migration --query Table.ItemCount
    gcloud spanner databases execute-sql migrationdb \ --instance=spanner-migration --sql="select count(*) from Migration"

    次の出力が表示されます。

    $ aws dynamodb describe-table --table-name Migration --query Table.ItemCount
    25000
    $ gcloud spanner databases execute-sql migrationdb --instance=spanner-migration --sql="select count(*) from Migration"
    25000
    
  4. 各テーブルのランダムなエントリをサンプリングして、データが一貫していることを確認します。

    gcloud spanner databases execute-sql migrationdb \
        --instance=spanner-migration \
        --sql="select * from Migration limit 1"
    

    次の出力が表示されます。

    $ gcloud spanner databases execute-sql migrationdb --instance=spanner-migration --sql="select * from Migration limit 1"
    Username    PointsEarned  ReminderDate  Subscribed  Zipcode
    aallen2538  1606          2018-06-18    False       17303
    
  5. 前の手順の Spanner クエリで返されたものと同じ Username を使用して、Amazon DynamoDB テーブルに対するクエリを実行します。例: aallen2538値はデータベースに固有となります。

    aws dynamodb get-item --table-name Migration \
        --key '{"Username": {"S": "aallen2538"}}'
    

    他のフィールドの値は、Spanner 出力の値と一致する必要があります。次の出力が表示されます。

    $ aws dynamodb get-item --table-name Migration --key '{"Username": {"S": "aallen2538"}}'
    {
        "Item": {
            "Username": {
                "S": "aallen2538"
            },
            "ReminderDate": {
                "S": "2018-06-18"
            },
            "PointsEarned": {
                "N": "1606"
            },
            "Zipcode": {
                "N": "17303"
            },
            "Subscribed": {
                "BOOL": false
            }
        }
    }
    

新しい変更をレプリケートする

一括インポート ジョブが完了したら、ストリーミング ジョブを設定して、進行中の更新をソーステーブルから Spanner に書き込みます。Pub/Sub のイベントに登録し、それを Spanner に書き込みます。

作成した Lambda 関数は、ソース Amazon DynamoDB テーブルへの変更を取得し、それを Pub/Sub に公開するよう構成されています。

  1. AWS Lambda がイベントを送信する宛先となる Pub/Sub トピックのサブスクリプションを作成します。

    gcloud pubsub subscriptions create spanner-migration \
        --topic spanner-migration
    

    次の出力が表示されます。

    $ gcloud pubsub subscriptions create spanner-migration --topic spanner-migration
    Created subscription [projects/your-project/subscriptions/spanner-migration].
    
  2. Pub/Sub に入ってくる変更をストリーミングして Spanner テーブルに書き込むには、Cloud Shell から Dataflow ジョブを実行します。

    cd ~/dynamodb-spanner-migration/dataflow
    mvn exec:java \
        -Dexec.mainClass=com.example.spanner_migration.SpannerStreamingWrite \
        -Dexec.args="--project=$GOOGLE_CLOUD_PROJECT \
                     --instanceId=spanner-migration \
                     --databaseId=migrationdb \
                     --table=Migration \
                     --experiments=allow_non_updatable_job \
        --subscription=projects/$GOOGLE_CLOUD_PROJECT/subscriptions/spanner-migration"
    
    1. ジョブの進行状況を確認するには、一括読み込みの手順と同様、Cloud Console で Dataflow に移動します。

      Dataflow に移動

    2. [Status] が [Running] のジョブをクリックします。

      ジョブの実行

      処理グラフには前と同様の出力が表示されますが、処理済みの各項目がステータス ウィンドウでカウントされます。システムの遅延時間は、Spanner テーブルに変更が反映されるまでに生じると思われる遅延の大まかな予想です。

      遅延時間によるプロセスの実行

一括読み込みフェーズで実行した Dataflow ジョブは、制限付きデータセットとも呼ばれる一連の有限の入力でした。この Dataflow ジョブは、Pub/Sub をストリーミング ソースとして使用しており、制限なしとみなされます。この 2 種類のソースの詳細については、Apache Beam プログラミング ガイドの PCollection の項を参照してください。このステップの Dataflow ジョブはアクティブのままにされるため、終了しても停止しません。ストリーミング Dataflow ジョブのステータスは、[Succeeded] ではなく [Running] のままになります。

レプリケーションを検証する

ソーステーブルになんらかの変更を加えて、その変更が Spanner テーブルにレプリケートされることを確認します。

  1. Spanner で存在しない行を照会します。

    gcloud spanner databases execute-sql migrationdb \
        --instance=spanner-migration --sql=\
        "SELECT * FROM Migration WHERE Username='my-test-username'"
    
  2. Spanner のクエリで使用したのと同じキーを使用して、Amazon DynamoDB にレコードを作成します。コマンドが正常に実行された場合、出力は生成されません。

    aws dynamodb put-item \
        --table-name Migration \
        --item '{"Username" : {"S" : "my-test-username"}, "Subscribed" : {"BOOL" : false}}'
    
  3. 元のクエリを再度実行して、その行が今は Spanner にあることを確認します。

    gcloud spanner databases execute-sql migrationdb \
        --instance=spanner-migration \
        --sql="SELECT * FROM Migration WHERE Username='my-test-username'"
    

    出力には挿入された行が表示されます。

    $ gcloud spanner databases execute-sql migrationdb --instance=spanner-migration --sql="SELECT * FROM Migration WHERE Username='my-test-username'"
    Username PointsEarned ReminderDate Subscribed Zipcode my-test-username None None False
  4. 元の項目の一部の属性を変更し、Amazon DynamoDB テーブルを更新します。

    aws dynamodb update-item \
        --table-name Migration \
        --key '{"Username": {"S":"my-test-username"}}' \
        --update-expression "SET PointsEarned = :pts, Subscribed = :sub" \
        --expression-attribute-values '{":pts": {"N":"4500"}, ":sub": {"BOOL":true}}'\
        --return-values ALL_NEW
    

    出力は次のように表示されます。

    $ aws dynamodb update-item \
    >   --table-name Migration \
    >   --key '{"Username": {"S":"my-test-username"}}' \
    >   --update-expression "SET PointsEarned = :pts, Subscribed = :sub" \
    >   --expression-attribute-values '{":pts": {"N":"4500"}, ":sub": {"BOOL":true}}'\
    >   --return-values ALL_NEW
    {
        "Attributes": {
            "Username": {
                "S": "my-test-username"
            },
            "PointsEarned": {
                "N": "4500"
            },
            "Subscribed": {
                "BOOL": true
            }
        }
    }
    
  5. 変更が Spanner テーブルに伝播されていることを確認します。

    gcloud spanner databases execute-sql migrationdb \
        --instance=spanner-migration \
        --sql="SELECT * FROM Migration WHERE Username='my-test-username'"
    

    出力は次のように表示されます。

    $ gcloud spanner databases execute-sql migrationdb --instance=spanner-migration --sql="SELECT * FROM Migration WHERE Username='my-test-username'"
    Username PointsEarned ReminderDate Subscribed Zipcode my-test-username 4500 None True
  6. Amazon DynamoDB のソーステーブルからテスト項目を削除します。

    aws dynamodb delete-item \
        --table-name Migration \
        --key '{"Username": {"S":"my-test-username"}}'
    
  7. 対応する行が Spanner テーブルから削除されていることを確認します。変更が伝播されると、次のコマンドはゼロ行を返します。

    gcloud spanner databases execute-sql migrationdb \
        --instance=spanner-migration \
        --sql="SELECT * FROM Migration WHERE Username='my-test-username'"
    

インターリーブ テーブルの使用

Spanner は、テーブルのインターリーブのコンセプトをサポートしています。これはトップレベルの項目に、そのトップレベル項目に関連する複数のネストされた項目があるという設計モデルです(お客様とその注文、プレーヤーとそのゲームスコアなど)。Amazon DynamoDB ソーステーブルで、ハッシュキーと範囲キーで構成される主キーを使用する場合は、次の図のように、インターリーブ テーブルのスキーマをモデリングできます。この構造では親テーブルのフィールドを結合しながら、インターリーブ テーブルを効率的に照会できます。

ユーザー テーブルと注文テーブルの比較

セカンダリ インデックスを適用する

データを読み込んだ後にセカンダリ インデックスを Spanner テーブルに適用することは、おすすめの方法のひとつです。レプリケーションはすでに機能しているため、セカンダリ インデックスを設定してクエリを高速化します。Spanner のセカンダリ インデックスは、Spanner テーブルと同様、完全整合性を備えています。ただし、多くの NoSQL データベースで一般的な結果整合性とは異なります。この機能はアプリの設計を簡素化するのに役立ちます。

インデックスを使用しないクエリを実行してみます。ここでは特定の列の値を指定して、上位 N 個のオカレンスを探します。これは Amazon DynamoDB における、データベースの効率性を高めるための一般的なクエリです。

  1. Spanner に移動

    Spanner に移動

  2. [QUERY] をクリックします。

    [Query] ボタン

  3. [Query] フィールドに次のクエリを入力し、[Run query] をクリックします。

    SELECT Username,PointsEarned FROM Migration
      WHERE Subscribed=true AND
      ReminderDate > DATE_SUB(DATE(current_timestamp()), INTERVAL 3 DAY)
    

    クエリが実行されたら、[Explanation] をクリックして、[Rows scanned] と [Rows returned] をメモします。インデックスがない場合、Spanner はテーブル全体をスキャンして、クエリに一致するデータの小さなサブセットを返します。

    スキャンされた行数と返された行数の比較

  4. これが一般的に発生するクエリを表している場合は、Subscribed 列と ReminderDate 列に複合インデックスを作成します。Spanner コンソールで、[Create Index] をクリックします。

  5. [Edit as Text] をクリックしてオンにします。

  6. [DDL statements] に、インデックス定義を入力します。

    CREATE INDEX SubscribedDate
    ON Migration (
      Subscribed,
      ReminderDate
    )
    
  7. バックグラウンドでのデータベース構築を開始するには、[Create] をクリックします。

    進行中のスキーマ更新

  8. インデックスが作成されたら、再度クエリを実行して、インデックスを追加します。

    SELECT Username,PointsEarned FROM
    Migration@{FORCE_INDEX=SubscribedDate}
      WHERE Subscribed=true AND
      ReminderDate > DATE_SUB(DATE(current_timestamp()), INTERVAL 3 DAY)
    

    クエリの説明をもう一度確認します。[Rows scanned] の数が減っていることを確認してください。各ステップで返される [Rows returned] は、クエリによって返される数と一致します。

    クエリの説明

インターリーブされたインデックス

Spanner ではインターリーブされたインデックスを設定できます。前のセクションで説明したセカンダリ インデックスは、データベース階層のルートにあり、従来のデータベースと同じ方法でインデックスを使用します。インターリーブされたインデックスは、インターリーブされた行のコンテキスト内にあります。インターリーブされたインデックスを適用する場所の詳細については、インデックス オプションをご覧ください。

データモデルの調整

このチュートリアルの移行部分を自分の状況に適応させるには、Apache Beam のソースファイルを変更してください。実際の移行ウィンドウ中には、ソーススキーマを変更しないようにしてください。そうでないと、データが失われる可能性があります。

  1. 受信 JSON を解析してミューテーションを構築するには、GSON を使用します。JSON 定義はデータに合わせて調整します。

    public static class Item implements Serializable {
        private Username Username;
        private PointsEarned PointsEarned;
        private Subscribed Subscribed;
        private ReminderDate ReminderDate;
        private Zipcode Zipcode;
    
    }
    
    public static class Username implements Serializable {
        private String s;
    
    }
    
    public static class PointsEarned implements Serializable {
        private String n;
    
    }
    
    public static class Subscribed implements Serializable {
        private String bOOL;
    
    }
    
    public static class ReminderDate implements Serializable {
        private String s;
    
    }
    
    public static class Zipcode implements Serializable {
        private String n;
    
    }
  2. 対応する JSON マッピングを調整します。

    mutation.set("Username").to(item.Username.s);
    
    Optional.ofNullable(item.Zipcode).ifPresent(x->{
        mutation.set("Zipcode").to(Integer.parseInt(x.n));
    });
    
    Optional.ofNullable(item.Subscribed).ifPresent(x->{
        mutation.set("Subscribed").to(Boolean.parseBoolean(x.bOOL));
    });
    
    Optional.ofNullable(item.ReminderDate).ifPresent(x->{
        mutation.set("ReminderDate").to(Date.parseDate(x.s));
    });
    
    Optional.ofNullable(item.PointsEarned).ifPresent(x->{
        mutation.set("PointsEarned").to(Integer.parseInt(x.n));
    });

前の手順では、Apache Beam のソースコードを一括インポート用に変更しました。次に同様の方法で、パイプラインのストリーミング部分のソースコードを変更します。最後に、Spanner ターゲット データベースのテーブル作成スクリプト、スキーマ、およびインデックスを調整します。

クリーンアップ

このチュートリアルで使用するリソースについて、Google Cloud Platform アカウントへの課金が発生しないようにする手順は次のとおりです。

プロジェクトの削除

  1. GCP Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

AWS リソースを削除する

このチュートリアル以外で AWS アカウントを使用する場合、次のリソースを削除する際は慎重に行ってください。

  1. Migration という名前の DynamoDB テーブルを削除する。
  2. 移行手順で作成した Amazon S3 バケットLambda 関数を削除する。
  3. 最後に、このチュートリアルで作成した AWS IAM ユーザーを削除する。

次のステップ