[AWS] DBに負荷をかけず約800万件のデータマイグレーションを実施した方法

2024/09/03に公開

3行まとめ

  • 同一DB内のテーブル間で約800万件のデータマイグレーションを実施
  • DB(Amazon Aurora)に対しての負荷をほぼかけることなく実施
  • マイグレーションは「SQS + Lambda」のサーバーレス構成で実施

データマイグレーション

概要

  • とある機能のリリースに合わせて既存テーブルから新規テーブルへのデータマイグレーションが必要となった
  • 1回のみ実施
  • データは約800万件
  • DBへの負荷は極力避けたい

技術スタック

  • AWS
    • Amazon Aurora
    • EC2
    • SQS
    • Lambda
  • 言語
    • PHP(Laravel)

実現方法の検討

- マイグレーション方法検討で候補にあがったのは以下

  1. Laravelのマイグレーション機能を利用する
  2. Laravelのシーダー機能を利用する
  3. EC2で移行スクリプト実行する
  4. Lambdaを利用する

1. Laravelのマイグレーション機能を利用する

概要

  • LaravelにはDBのバージョン管理的な機能が備わっています
  • 主なユースケースは「DDL」の発行かと思います
  • この機能を使いマイグレーションファイルの中に今回のデータマイグレーション処理を行う事を検討した

不採用理由

  • Laravelマイグレーションは「リリース工程」の一部
  • 約800万件のマイグレーションは相当時間がかかる
  • リリース時間がかなり長時間になってしまい他のリリースへの影響も考えられる
  • 1度きりのマイグレーション処理が半永久的にシステム内に残存してしまう

2. Laravelのシーダー機能を利用する

概要

  • Laravelにはデータをシード(種をまく、初期値の設定)する機能を持っています
  • 主なユースケースはマスターテーブル等への「初期値の設定」かと思います
  • この機能を使いシーダークラスの中に今回のデータマイグレーション処理を行う事を検討した

不採用理由

※Laravelマイグレーションと同理由なので割愛

3. EC2で移行スクリプト実行する

概要

  • 弊システムではEC2からRDSへ接続が可能となっている
  • それを利用してEC2上でマイグレーションスクリプトを実行することで実現する

不採用理由

  • EC2上で長時間スクリプトが実行状態となる
  • 仮に実行速度を早めたくなった場合に並列実行などを組み込むのに一手間必要
  • 実行失敗対象の再実行性の考慮も難しい(と思われる)
  • EC2が何かしらの原因で落ちてしまう可能性を考慮

4. Lambdaを利用する

概要

  • SQSにデータマイグレーション対象を投入
  • LambdaがSQSから処理対象のメッセージを取得しデータマイグレーション
  • 処理失敗したらSQS DLQへメッセージを移動

採用理由

  • メッセージ受信数無制限のSQSへ処理対象を投入することで処理対象が何万件あっても大丈夫
  • 処理失敗したものをDLQに移せることで再処理も容易
  • トリガーの設定でSQSからの取得メッセージを制御できる

構築

AWS構成図

  • 今回は以下の構成で作成していく

AWS構成図

DB負荷を考慮したLambdaトリガーの設定と処理

  • 今回はLambdaの起動数調整と処理間隔調整によりDB負荷を調整できるようにした

Lambdaトリガーによる負荷調整

以下の値を設定し1回のLambda起動で最小の処理に抑える

  • BatchSize
    • 1回で受信するメッセージの数
    • 今回は1起動で1メッセージずつ処理をしたいので「1」を設定
    • ※必ずしも設定した値でメッセージ数が取得できるとは限らない事に注意
  • MaximumConcurrency
    • Lambdaの同時に実行される最大数を制御できる
    • 最小値は「2」であり今回はこの値を設定

Lambda処理による負荷調整

  • マイグレーション処理の「データ登録」「データ削除」の処理に指定時間インターバルを設けられるようにした
  • Lambdaの環境変数に設定し関数内部でその値を使用して sleep させる
  • 1回データ登録後に指定時間 sleep させ、その後処理が再開される
  • これにより時間操作でDB負荷を操作する

AWS SAMによるSQSとLambdaの構築

※SAMのインストール、ローカル開発環境などについては以下記事をご参照ください
https://zenn.dev/third_tech/articles/602e97a68f3370

SAMテンプレート

  • テンプレートにSQSとLambdaの構成を記述する

※テンプレートはあくまでサンプルであり適宜必要に応じて修正が必要です

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  migrate-app
  Function:
    Timeout: 60
    MemorySize: 128
    Runtime: python3.9
    Tracing: Active
Resources:
  MigrationAppQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: migration-app-queue
      VisibilityTimeout: 60
      ReceiveMessageWaitTimeSeconds: 20
      MessageRetentionPeriod: 1209600 # 14 days
      RedrivePolicy:
        deadLetterTargetArn: !GetAtt MigrationAppDeadLetterQueue.Arn
        maxReceiveCount: 1 # 1回受信して失敗した時点でDLQへGo
  MigrationAppDeadLetterQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: migration-app-dead-letter-queue
  MigrationAppFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: app.lambda_handler
      CodeUri: migration_app
      Description: Migration App function
      Architectures:
      - x86_64
      Role: !GetAtt LambdaExecutionRole.Arn
      Layers:
        - !Sub arn:aws:lambda:${AWS::Region}:133490724326:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11
      Events:
        SqsEvent:
          Type: SQS
          Properties:
            Queue: !GetAtt MigrationAppQueue.Arn
            BatchSize: 1 # 1メッセージずつ受け取る設定
            ScalingConfig:
              MaximumConcurrency: 2 # Lambda同時実行の最大数を制御する
      Environment:
        Variables:
          POWERTOOLS_SERVICE_NAME: PowertoolsMigrationApp
          POWERTOOLS_METRICS_NAMESPACE: Powertools
          LOG_LEVEL: INFO
          STOP_TIME_FOR_DELETE: 5 # Lambdaコード内でデータ削除間隔を制御(秒)
          STOP_TIME_FOR_INSERT: 3 # Lambdaコード内でデータ登録間隔を制御(秒)
      Tags:
        LambdaPowertools: python
  LambdaExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      Path: '/'
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
        - "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess"
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
  SSMAndKMSAccessManagedPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: SSMAndKMSAccessManagedPolicy
      Path: /
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - 'ssm:Get*'
              - 'kms:Decrypt'
            Resource: "*"
      Roles:
        - !Ref LambdaExecutionRole

Lambdaアプリケーションコード

  • データマイグレーションする処理を記述します

※今回は詳細なコード記載については割愛します

デプロイ

  • AWS環境へデプロイするにはAWS認証情報が必要です
    • AWS環境変数やCredentialsファイルやProfileなどいろんな方法があるので各環境に合わせた方法で対応

※デプロイコマンド例

# AWS認証情報を環境変数にセット
export AWS_ACCESS_KEY_ID="xxxxxxxxxxxxxxxx"
export AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxx"
export AWS_SESSION_TOKEN="xxxxxxxxxxxxxxxxxxx"

# デプロイパッケージの作成
sam build

# AWSへデプロイ(今回IAM作成があるのでオプションを指定)
sam deploy --capabilities CAPABILITY_NAMED_IAM

マイグレーション実施

SQSへ処理対象をキュー

※SQSへメッセージ送信例
(実際はスクリプトを作成し処理対象分を一気にSQSへキューしています)

# 最大10件メッセージ送信するコマンド
aws sqs send-message-batch --region ap-northeast-1 --queue-url "$queue_url" --entries "$batch_messages"

SQSメトリクス

上記メトリクスから一気に数万件の処理対象が送信されたことが分かります

Lambdaが実行されているか確認

  • Lambdaのメトリクスやログから実行が問題ないか確認

Lambdaメトリクス

上記メトリクスからエラー無く全ての処理を実行し終えたことが分かります

DB負荷確認

  • RDSのメトリクスやログから実行が問題ないか確認

RDSメトリクス

上記メトリクスからマイグレーション実施期間の「8月29日」から「9月1日」において CPU使用率 の変化は殆ど見られないことが分かります
意図通りの負荷で処理を完了させることが出来ました

最後に

無事にDBに負荷をかけることなくデータマイグレーションを完了させることが出来ました。

SQSとLambdaのパターンを用いることで「並列実行」「同時実行」などの本来複雑な部分をAWS側の設定で簡単に制御させることが出来るのは非常にありがたいです。

今回検討したマイグレーション方法以外にも、AWSには多様なマイグレーションサービスやツールが用意されているため、ケースに応じた選定がとても大事になると思います。

  • AWS DMS
  • AWS Glue
  • RDSからS3へのエクスポート
    などなど

今回の方法が何かしらのお役に立てばと思います。

株式会社THIRD エンジニアブログ

Discussion