Amazon Web Services ブログ

Amazon DynamoDB へのリファクタリング

リレーショナルデータベースから NoSQL への移行を考えていますか? 以下の記事では、Amazon EC2 インスタンスから Amazon DynamoDB への SQL Server データの読み取り、変換、書き込みについて詳しく説明します。AWS Glue を使用して、DynamoDB で複数のテーブルのソースデータモデルを 2 つのターゲットテーブルに変換します。

AWS Glue の代わりに、AWS DMSAWS Marketplace ツールなど、データモデル変換のためのその他のオプションがあります。この 1 回限りの移行では、複数のテーブルを 1 つに変換するために AWS Glue および Scala コードを選択しました。

概要

このデモでは、商用リレーショナルデータベースをリファクタリングする方法を示します。選択したデータベースは、スポーツイベントのチケットを生成および販売します。データベースを DynamoDB にリファクタリングするためのベストプラクティスと、DynamoDB テーブルを設定し、AWS Glue を使用してデータを転送する方法を説明します。また、VPC エンドポイントと AWS Glue のセキュアな IAM ロールを設定する方法、ソースデータベースをクロールし、Apache Spark ETL ジョブを実行する方法も示します。

すぐに開始するには、DynamoDB リポジトリへのリファクタリングのコードにアクセスしてください。

問題と提案される解決策

環境内で AWS CloudFormation スタックを起動すると、スタックは常駐チケットデータベースを使用して EC2 インスタンスを作成します。

データベース DMS_SAMPLE には、スポーツ会場、リーグ、チーム、プレーヤーについての一連の設定データが含まれています。これらの従業員が管理するテーブルのデータは、頻繁には変更されません。専用の設定アプリケーションが、こうしたテーブルにデータを書き込みます。SPORTING_EVENT_TICKET、TICKET_PURCHASE_HIST データベースでチケット販売のテーブルを確認することもできます。このデータベースは、スポーツイベントチケットをカタログ化、販売、転送します。

従業員と消費者は新しいイベントチケットを継続的に売買するため、このデータは頻繁に変更されます。チケット販売の開始後、ピークトラフィック量がサイトに流れます。データベースは、この変動する需要に合わせて拡張する必要があります。次の図は、データベーススキーマを示しています。

このデモで検討したイベントチケットシステムには、チケットの販売と取得という 2 つの異なるユースケースが含まれています。それぞれのユースケースは、個別のアクセスパターンを示しています。

チケット販売活動は大きく変動します。システムの定常状態は、予想される 1 時間あたり数百件のリクエストに対応できるように設定されています。ただし、ピークイベント時には、リクエストが 1 分間に数十万件に急増する可能性があります。またこのサービスは、チケットを再販業者に販売し、再販業者は他の個人に再販します。自動化された再販売システムは、まれに発生する予測不可能なアクティビティスパイクを引き起こす可能性もあります。

ユーザーは通常、次の基準とアクセスパターンに基づいてチケットを取得します。

  1. どのチケットを所有しているか?
    • 自分が所有するすべてのチケットを取得する
    • 会場と日付で利用可能なすべてのチケットを取得する
    • チケットを購入する
    • 別のユーザーにチケットを転送する
  1. どのチケットを他の人に転送しましたか?
    • 別のユーザーに転送したチケットを取得する
    • 別のユーザーから自分に転送されたチケットを取得する

設定データを書き込むアプリケーションは、ベースラインでうまく機能します。ただし、新しいイベントのチケット販売トラフィックのピークは、リレーショナルデータベースシステムに負担をかけます。ユーザーがイベントを追加するにつれて、負荷を処理するためにデータベースリソースを増やす必要があります。

一方で、その後チケット販売が減少し、コンピューティング能力が過剰になります。トラフィックの予測は難しいため、ユーザーは現在のシステムよりも柔軟に容量を増減できる方法が必要です。

一般に、リレーショナルデータベースシステムは、以下の理由でスケーラビリティが不十分です。

  • 複数のテーブルにデータを保存しており、ディスクへの読み書きに複数の照会が必要である
  • 大規模な ACID 準拠リレーショナルトランザクションシステムのパフォーマンスコスト
  • クエリ結果のビューを再構築するために必要な結合が高価である

したがって、問題は、変動する需要を満たすためにユーザーはどのようにしてより効率的にスケーリングできるのか?ということになります。

提案される解決策である DynamoDB はシームレスなスケーラビリティを備えた完全マネージド型の NoSQL データベースサービスです。DynamoDB は、ピークに対する高速スケールアウトと谷に対する高速スケールインを提供します。一貫したパフォーマンスを提供し、アプリケーションへのトラフィックが増加すると自動的にスケーリングします。DynamoDB の自動スケーリングは、実際のトラフィックパターンに応じて、プロビジョニングされたスループット容量を動的に調整します。

以下の理由により、DynamoDB は適切にスケールされます。

  • スキーマの柔軟性により、DynamoDB は単一のアイテム内に複雑な階層データを保存できます。
  • 複合キーの設計により、DynamoDB は関連するアイテムを同じテーブルにまとめて保存できます。
  • 自動シャーディングにより水平スケールが可能になるので、データは多くのサーバーで同時に処理され、リソースの透過性が高まります。

DynamoDB を使用すると、高パフォーマンスの読み取りと書き込みを管理できます。ワークロードに適したスキーマを設計すると、負荷が大幅に変動しても一貫したパフォーマンスを期待できます。

DynamoDB によるデータの転送

このセクションでは、以下の方法を示します。

  1. DynamoDB テーブル構造を設定する。
  2. 環境に合わせてファイルを設定する。
  3. AWS CloudFormation を使用してリソースを作成する。
  4. ソースデータベースでデータを生成する。
  5. AWS Glue でデータを抽出、変換、ロードする。

前提条件

このデモで提供されているコードに従って実行するには、以下が必要です。

環境を順守するには、リポジトリからファイルをダウンロードし、順番に手順を実行します。移動するデータの量によっては、無料利用枠リソースを使用できる場合があります。

一般に、リレーショナルデータベースを DynamoDB に移行する前に、以下を確認してください。

  • サポートする予定のユースケースまたはビジネス機能を理解する。
  • 典型的なデータアクセスパターンを理解する。
  • 最適化しようとしているシステムの目標、問題、側面について合意する。

追加情報については、DynamoDB のベストプラクティスを参照してください。

DynamoDB テーブル構造を設定する

DynamoDB を使用すると、生成するチケットを販売するチケットと単一のテーブルに結合し、ステータス/所有者を使用してチケットの現在の状態を追跡できます。次のスクリーンショットは、結合チケット情報テーブルを示しています。

設定データには、オーバーロードされたテーブル構造を使用します—つまり、複数のオブジェクトを単一のテーブルに結合する構造です。このテーブル構造は、チケットの生成を追跡するためのデータを提供します。

次のスクリーンショットは、いくつかのテーブルがこのデータをどのようにモデル化するかを示しています。パーティションキーを使用して、同様のデータと、設定データの一意の値の並べ替えキーをグループ化します。わかりやすい名前フィールドを使用して、名前 (GSI) でアイテムを検索したり、対象となる照会のスパース GSI 値としてキーワードを含むその他のフィールドを検索したりできます。

環境に合わせてファイルを設定する

次に、リファクタリングのために環境を設定します。

  1. GitHub リポジトリからローカルマシンにファイルをまだダウンロードしていない場合は、ここでダウンロードします。次に、parameters.json ファイルを編集して、AWS アカウント環境からの情報で以下の値を更新します。
    1. KeyName パラメータを EC2 キーペア名で置き換えます。EC2 キーを使用して、AWS CloudFormation によって起動された EC2 インスタンスにログインし、SQL Server データベースを保存することに注意してください。
    2. IPAddressForRDP パラメータ (デフォルトは 0.0.0/0) を使用している IP アドレスで置き換えます。https://www.whatsmyip.org を使用して IP アドレスを取得することができます。
    3. <bucket name> の S3ScriptLocation パラメータ値を S3 バケット名で置き換えます。
    4. <bucket name> の S3LocationForJarFiles パラメータ値を S3 バケット名で置き換えます。
  2. キー <S3 bucketname>/scripts の下で、環境内の S3 バケットに以下のファイルをアップロードします。
    RDBMStoDynamoDB.yml
    parameters.json
    config.scala 
    ticketsale.scala 
  3. 従う必要がある依存関係を取得するには、次のコマンドを使用してリポジトリ awslabs / emr-dynamodb-connector のクローンを作成します。
    git clone https://github.com/awslabs/emr-dynamodb-connector.git
    1. クローン作成が完了したら、次のコマンドを使用してリポジトリを構築します。
      mvn clean install
    2. 前の手順と同じ S3 バケットを使用して、キー <S3 bucketname>/jars の下に以下の JAR ファイルをアップロードします。
      emr-dynamodb-connector/emr-dynamodb-hadoop/target
      /emr-dynamodb-hadoop-4.9.0-SNAPSHOT.jar 
      emr-dynamodb-connector/emr-dynamodb-hive/target/emr-dynamodb-hive-4.9.0-SNAPSHOT-jar-with-dependencies.jar

これで、AWS CloudFormation を使用してリソースを作成する準備ができました。

AWS CloudFormation を使用してリソースを設定する

CLI で以下のコマンドを使用して、AWS CloudFormation テンプレートからスタックを作成します。スタック名と S3 ロケーションを、使用している環境の該当する情報に置き換えます。

aws cloudformation create-stack --stack-name <stack name> \
    --template-url https://your-bucket-name.s3.amazonaws.com/scripts/RDBMStoDynamoDB.YAML
    --parameters https://your-bucket-name.s3.amazonaws.com/scripts/parameters.json \
    --capabilities CAPABILITY_NAMED_IAM

ソースデータベースでデータを生成する

ユースケースのデータを生成するには、EC2 インスタンスにログインしてクエリを実行する必要があります。EC2 インスタンスの IP アドレスを取得して接続する手順は次のとおりです。

  1. 次のコマンドを使用してインスタンス ID を取得します。
    aws cloudformation describe-stacks --stack-name <stack name> \
    --query "Stacks[0].Outputs[?OutputKey=='EC2InstanceID'].OutputValue" --output text
  2. 次のコマンドを使用して、パスワードデータを復号化して取得します。
    aws ec2 get-password-data --instance-id <Instance ID> \
    --priv-launch-key <Location to pem file>
  3. 次のコマンドを使用して、出力セクションから EC2 インスタンスのパブリック IP アドレスを取得します。
    aws cloudformation describe-stacks --stack-name <stack name> \
    --query "Stacks[0].Outputs[?OutputKey=='PublicIPOfEC2InstanceWithSQLServer'].OutputValue" --output text

リモートデスクトップを使用すると、管理者としてのユーザー名と前のコマンドのパスワードデータで、IP アドレスを使用して EC2 インスタンスにサインインできます。

  1. 次の認証情報を使用して、インスタンスタスクバーから SQL Server Management Studio を起動します。
    awssct/Password1
  2. 次のクエリを実行します。
    USE dms_sample
    BEGIN 
        -- 大量のデータを生成するには、while 条件を、
        -- チケットを生成するイベントの数の値に変更します
        DECLARE @generatetkccnt INT = 0;
        WHILE @generatetkccnt < 10    
        BEGIN
            EXEC dbo.generate_tickets @event_id = @generatetkccnt 
            SET @generatetkccnt = @generatetkccnt + 1;
        END;
        -- このループは約 10 セットのチケットを販売し、
        -- チケットの販売数を増やすにはカウンターを増やします 
        DECLARE @ticketcnt INT = 0;
        WHILE @ticketcnt < 10
        BEGIN 
            EXEC generateTicketActivity @max_transactions = @ticketcnt
            SET @ticketcnt = @ticketcnt + 1;  
        END;
        -- このループは約 5 セットのチケットを転送し、
        -- チケットの転送数を増やすにはカウンターを増やします  
        DECLARE @tickettransfercnt INT = 0;
        WHILE @tickettransfercnt < 10 
        BEGIN       
            EXEC generateTransferActivity @max_transactions = @tickettransfercnt
            SET @tickettransfercnt = @tickettransfercnt + 1
        END;
    END;

このクエリはチケットデータを生成し、次の手順に進むことができます。

  1. 次のクエリを使用してチケットアクティビティを確認します。
    -- 販売を確認するクエリ     
    SELECT * FROM dms_sample.dbo.ticket_purchase_hist;
    -- 転送を確認するクエリ    
    SELECT * FROM dms_sample.dbo.ticket_purchase_hist where transferred_from_id IS NOT NULL;

AWS Glue でデータを抽出、変換、ロードする

AWS Glue は完全マネージド型 ETL サービスです。ETL ジョブは、AWS マネジメントコンソールでのいくつかの手順で、または AWS CLI または AWS SDK を使用したコードで作成して実行することができます。

このセクションでは、AWS Glue を使用して DynamoDB ターゲットに接続し、S3 を介してプライベートにデータを転送する方法を示します。

  1. AWS Glue クローラを起動して、dms_sample データベースをクロールし、メタデータを取得します。このプロセスにより、AWS Glue カタログにソースデータベースとソーステーブルに関する情報が入力されます。
    aws glue start-crawler --name GlueCrawlerForSqlServer

    クローラは JDBC 接続を使用して SQL Server に接続し、数分で AWS Glue データベースにメタデータのテーブルを追加します。

  1. 次のテーブル取得コマンドを使用して、テーブルに関する詳細を取得します。
    aws glue get-tables --database-name gluedatabase
  1. チケット販売のために AWS Glue ジョブ GlueTicketSaleJob を実行します。このジョブは、SQL Server ソースデータベースから読み取り、SPORTING_EVENT_TICKET および TICKET_PURCHASE_HIST からデータを抽出します。次に、この記事の冒頭で概説したように、ジョブは ticketsale.scala の Scala コードを使用して、2 つのテーブルを単一の DynamoDB テーブル ticket_sales に変換します。
    aws glue start-job-run --job-name GlueTicketSaleJob

    このコマンドからの出力として JobRunId 値を受け取るはずです。

    {
     "JobRunId": "jr_f68bfd90a1fd16d24a7e2143aca20cb9c0b0472185d241b5cea37918f8de4c15"
    }

また、サポートする設定データの 2 番目の AWS Glue ジョブである GlueConfigJob を並行して開始することもできます。このジョブは、SQL Server ソースデータベースから読み取り、SQL Server データベース内の他のすべての設定テーブルからデータを抽出します。このジョブは Scala コード config.scala を使用して、設定テーブルをオーバーロードされた単一の DynamoDB テーブル config に変換します。

aws glue start-job-run --job-name GlueConfigJob

このコマンドからの出力として新しい JobRunId を受け取ります。

{
 "JobRunId": "jr_f30cd7703a9554e8215975c7f3e8b44a3f84f6c4c724c094bd4f4ca0a40f8b2e"
}

AWS CLI を使用して、ジョブのステータスを監視できます。

aws glue get-job-runs --job-name GlueTicketSaleJob

出力には、このジョブに対して実行されたジョブがリストされます。ジョブの完了には時間がかかる場合がありますが、ジョブが完了すると、JobRunState が COMPLETE と表示されます。

{
 "JobRuns": [
 {
 "Id": "jr_f68bfd90a1fd16d24a7e2143aca20cb9c0b0472185d241b5cea37918f8de4c15",
 "Attempt": 0,
 "JobName": "GlueTicketSaleJob",
 "StartedOn": 1558363337.602,
 "LastModifiedOn": 1558363341.778,
 "JobRunState": "RUNNING",
 "PredecessorRuns": [],
 "AllocatedCapacity": 10,
 "ExecutionTime": 0,
 "Timeout": 2880,
 "MaxCapacity": 10.0,
 "LogGroupName": "/aws-glue/jobs"
 }
 ]
}

ジョブが完了すると、DynamoDB で結果を確認できます。次のスクリーンショットは、サンプルの設定データを表しています。

次のスクリーンショットは、サンプルのチケット販売データを示しています。

AWS CloudFormation 概要

AWS Glue ジョブ用に作成したアイテムを確認し、すべてのコンポーネントが AWS CloudFormation とどのように適合するかを検討します。

次の図は、セキュリティグループによって保護されたパブリックサブネット内の EC2 インスタンスを示しています。セキュリティグループには、AWS CloudFormation テンプレートで指定された CIDR 範囲へのルートと、AWS Glue に対する自己参照ルールがあります。VPC エンドポイントを通じて DynamoDB および S3 にアクセスできます。データは EC2 インスタンスの SQL Server インスタンスから AWS Glue および S3 を介して DynamoDB テーブルに流れます。

Amazon S3 および DynamoDB 用の VPC エンドポイント

AWS CloudFormation テンプレートの次の部分は、VPC をプライベートに Amazon S3 に接続するための VPC エンドポイントを提供します。S3 用の VPC エンドポイントにより、AWS Glue はプライベート IP アドレスを使用して、パブリックインターネットに曝されることなく S3 にアクセスできます。このリージョン内の S3 へのリクエストは、AWS ネットワーク内のプライベート S3 エンドポイントにルーティングされます。

  S3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - '*'
            Resource:
              - '*'
      VpcId: !Ref Vpc
      ServiceName: !Join 
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .s3
      RouteTableIds: 
        - !Ref PublicRouteTable

AWS CloudFormation テンプレートの次の部分は、DynamoDB 用の VPC エンドポイントを作成します。AWS Glue が DynamoDB に書き込むために必要です。

DynamoDEndPoint:
    Type: AWS::EC2::VPCEndpoint
    Properties: 
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - '*'
            Resource:
              - '*'
      VpcId: !Ref Vpc
      ServiceName: !Join
        - ''
        - - com.amazonaws.
          - !Ref 'AWS::Region'
          - .dynamodb
      RouteTableIds: 
        - !Ref PublicRouteTable

侵入ルールの設定

AWS Glue がコンポーネント間で通信するには、ソースを VPC 内の同じセキュリティグループに制限し、他のネットワーク上のトラフィックを防ぐ自己参照侵入ルールが必要です。侵入ルールを設定するには、以下のコードを実行します。

SelfReferencingIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties: 
      GroupId: !Ref InstanceSecurityGroup
      Description: Allow access between security groups.
      IpProtocol: tcp
      FromPort: '0'
      ToPort: '65535'
      SourceSecurityGroupId: !Ref InstanceSecurityGroup

IAM ロールのプロビジョニング

AWS Glue ジョブはログを Amazon CloudWatch に書き込み、一時ログを S3 バケットに保存します。また、AWS Glue は、S3 バケット内のサポート jar ファイルにもアクセスする必要があります。AWS Glue クローラには、EC2 インスタンスにアクセスして EC2 の SQL Server のソースデータをクロールするためのアクセス許可が必要です。

また、ロールには AWS Glue のフルアクセスもあり、AWS Glue コンポーネントが相互に通信できるようにします (たとえば、AWS Glue クローラは AWS Glue 接続にアクセスする必要があります)。IAM ロールアクセスを提供するには、以下のコードを実行します。

GlueRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "glue.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AmazonEC2FullAccess'
        - 'arn:aws:iam::aws:policy/AmazonS3FullAccess'
        - 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'  
        - 'arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess'
        - 'arn:aws:iam::aws:policy/AWSGlueConsoleFullAccess'
      RoleName: GlueConnectionRole

AWS Glue と SQL を接続する

AWS Glue 接続は、EC2 上の SQL Server データベースに接続するための新しい JDBC 接続を作成します。詳細については、AWS Glue 接続を参照してください。

GlueConnectionToSQLServer:
    Type: AWS::Glue::Connection
    Properties:
      CatalogId: !Ref AWS::AccountId
      ConnectionInput:
        Description: "Connect to SQL server database."
        Name: GlueConnectionToSQLServer
        ConnectionType: "JDBC"
        PhysicalConnectionRequirements:
          AvailabilityZone: 
            Fn::Select:
              - 0
              - Fn::GetAZs: ""
          SecurityGroupIdList:
            - !Ref InstanceSecurityGroup
          SubnetId: !Ref PublicSubnet
        ConnectionProperties: 
          "USERNAME": awssct
          "PASSWORD": Password1
          "JDBC_ENFORCE_SSL" : "false"
          "JDBC_CONNECTION_URL": !Sub
            - jdbc:sqlserver://${IpAddress}:1433;databaseName=dms_sample 
            - { IpAddress: !GetAtt EC2SourceDB.PrivateDnsName }

AWS CloudFormation テンプレートの次の部分は、クローラがクロールされたメタデータを保存する AWS Glue データベースを作成します。

GlueDatabase:
    Type: AWS::Glue::Database
    Properties:
      DatabaseInput: 
        Name: gluedatabase
      CatalogId: !Ref AWS::AccountId

ソースデータベースのクロール

AWS Glue は、dms_sample データベースをクロールするための AWS Glue クローラを作成します。クロールされたテーブルには Crawler_ss というプレフィックスが付けられ、前のセクションで作成された AWS Glue データベースに保存されます。

GlueCrawler:
    Type: AWS::Glue::Crawler
    Properties:
      Role: !GetAtt GlueRole.Arn
      Description: "Used to crawl the RDS for metadata"
      DatabaseName: !Ref GlueDatabase
      Targets: 
        JdbcTargets:
          - ConnectionName: !Ref GlueConnectionToSQLServer
            Path: "dms_sample/dbo/%"
      TablePrefix: "Crawler_ss" 
      Name: GlueCrawlerForSqlServer

Apache Spark ETL ジョブの実行

AWS Glue ジョブは、指定された S3 ロケーションでスクリプトを取得し、オンデマンドで実行します。Apache Spark ETL ジョブにコマンド名 glueetl を使用します。

GlueJob: 
    Type: AWS::Glue::Job
    Properties:
      Command: 
        Name: glueetl
        ScriptLocation: !Ref S3ScriptLocation
      ExecutionProperty:
        MaxConcurrentRuns: 2
      MaxRetries: 0
      Name: GlueJob
      Role: !GetAtt GlueRole.Arn

まとめ

ここまで、AWS Glue を使用して、チケット発行データベースを SQL Server RDBMS から DynamoDB に移行する方法を見てきました。使用しているアプリケーションで同じことを行うには、DynamoDB のベストプラクティスを参照してください。Refactoring-to-DynamoDB GitHub リポジトリからコード例をダウンロードし、実際のアプリケーションと環境に合わせて修正してください。

いつものように、AWS では皆さんのフィードバックをお待ちしています。コメントまたはご質問については、以下から送信してください。

 


著者について

Wendy Neu は、2015 年 1 月から Amazon でデータアーキテクトとして活躍しています。 Amazon 入社前は、オハイオ州シンシナティでコンサルタントとして働いており、お客様が関連性のないさまざまなデータソースからのデータを統合し管理するお手伝いをしていました。

 

 

Soumya Vanga は、ニューヨーク州ニューヨークの AWS プロフェッショナルサービスのアソシエイトクラウド開発者であり、お客様がソリューション、ワークロードを設計し、クラウドネイティブなサービスを採用するお手伝いをしています。