[UPDATE] Amazon Redshift Concurrency Scaling(同時実行スケーリング)が書き込みクエリをサポート、更に進化したオートスケールを実際に試してみました!

2021.08.06

データアナリティクス事業本部コンサルティングチームの石川です。先日、Amazon Redshift の Cluster Version 1.0.28965からConcurrency Scaling(同時実行スケーリング)が、INSERT、DELETE、UPDATE、COPYの書き込みクエリを追加サポートしました。書き込みクエリのサポートによって、ワークロードに応じて素早くスケーリングできるので、よりサーバレスのような従量課金に近い利用が可能になりました。今回はELTワークロードで最も利用頻度の高いINSERT INTO SELECTを同時60クエリで試してみます。

Concurrency Scalingとは

Concurrency Scaling(同時実行スケーリング)は、同時実行クエリの数に応じてクラスタをオートスケールする機能です。SELECTクエリは2年以上前に利用可能となり、過去にアーキテクチャや機能、パフォーマンス検証をご紹介しています。なお、2年前と比べて参照系クエリのスループット、クラスタのスケールアウト/スケールインの速度がともに向上しています。

RDS Auroraでもデータの書き込みノードはシングルノードですが、Redshiftはクラスタ単位でオートスケールすることでデータの読み書きのクエリもスケーリングをします。料金は、従来と変わらずスケーリングクラスタ上でクエリが実行された時間に応じた秒単位の従量課金です。

Concurrency Scaling利用すると、これまでピーク時のワークロードに合わせてプロビジョニングしていたクラスタを必要に応じてパフォーマンスをスケールするようにコスト最適化できます。

Concurrency Scalingを開始するには

同時実行スケーリングモード

ワークロード管理の画面で、同時実行スケーリングモードを自動に指定します。自動に設定すると後述のmax_concurrency_scaling_clusters(同時実行クエリ数)の範囲で、クエリのリクエスト数に応じて自動的にスケーリングクラスタがスケールイン・スケールアウトします。

max_concurrency_scaling_clustersの設定

パラメータグループ(max_concurrency_scaling_clusters)にスケーリングクラスタの最大数を指定します。下記の例では、スケーリングクラスタの上限を「10」に設定しています。なお、スケーリングクラスタの上限は、AWSサポート経由で上限緩和申請が可能です。

  • 0-10の範囲でスケールするクラスタ数を指定する。

書き込みクエリの制限事項

  • RA3ノード(ra3.16xlarge、ra3.4xlarge、ra3.xlplus)のみ、書き込みクエリをサポートします
  • identityカラムを持つテーブルへの書き込みクエリをサポートしていません
  • DISTSTYLEがALLに設定されているテーブルの書き込みクエリはサポートしていません
  • コミットブロック内にCREATE TABLEALTER TABLEなどのDDLステートメントはサポートしていません

検証条件

今回は、max_concurrency_scaling_clustersの値を「0」(スケーリングなし)と「10」(スケーリングクラスタの最大数10)に設定したクラスタに対して、同様の更新系クエリを実行しました。テーブルロックを発生させないために事前に10000テーブルを用意して、INSERT INTO SELECTにてSELECTクエリの結果をターゲットテーブルにINSERTします。スケーリング条件を明確にするため、Manual WLMとしました。以下の観点で検証とその結果を評価します。

  • スループット
  • クエリのレスポンス
    • Main Cluster
    • scaling Cluster
  • スケーリング
    • スケールアウト
    • スケールイン
  • コスト

検証環境

  • Redshift Cluster
    • Cluster identifier : csw-cluster
    • Node type : ra3.xlplus
    • Number of nodes : 2
    • Current cluster version : 1.0.29306
    • Region : Tokyo (ap-northeast-1)
  • Workload management
    • Manual WLM
    • Default queue
    • Memory (%) : 100
    • Concurrency on main : 5
    • Concurrency scaling mode : auto

検証:スケーリングクラスタ:なし

Parameters

  • max_concurrency_scaling_clusters : 0

Query Setting

  • Query (counts) : 10000
  • Concurrency : 60
  • Source Table : lineorder_all (2.4 billion records)
  • Target Tables : lineorder_%s (10000 tables)
  • enable_result_cache_for_session : false (user scope)
String _sql = String.format("INSERT INTO lineorder_%s SELECT lo_orderdate, sum(lo_extendedprice), sum(lo_ordertotalprice) FROM lineorder_all WHERE lo_orderdate > %d AND lo_orderdate < %d GROUP BY 1 ORDER BY 3 DESC LIMIT 1;", 
    threadNumber, 
    startDate, 
    endDate);

Result

  • first query starttime : 2021/8/3 23:35:51
  • Elapsed: 11:36:43 (43032026 ms)
  • Total Query Count : 10000
  • Main cluster
    • Query count : 10000
  • Average Elapsed : 00:03:15 (254752 ms)
  • scaling Cluster
    • Query count : 0
    • Average Elapsed : n/a
    • first query endtime by scaling cluster : n/a

Metrics - Cluster performance

Metrics - Database Performance

Metrics - Workload Concurrency

検証:スケーリングクラスタ:10

Parameters

  • max_concurrency_scaling_clusters : 10

Query Setting

  • Query (counts) : 10000
  • Concurrency : 60
  • Source Table : lineorder_all (2.4 billion records)
  • Target Tables : lineorder_%s (10000 tables)
  • enable_result_cache_for_session : false (user scope)
String _sql = String.format("INSERT INTO lineorder_%s SELECT lo_orderdate, sum(lo_extendedprice), sum(lo_ordertotalprice) FROM lineorder_all WHERE lo_orderdate > %d AND lo_orderdate < %d GROUP BY 1 ORDER BY 3 DESC LIMIT 1;", 
    threadNumber, 
    startDate, 
    endDate);

Result

  • first query starttime : 2021/8/3 11:55:33
  • Elapsed: 00:33:18 (1997828 ms)
  • Total Query Count : 10000
  • Main cluster
    • Query count : 465
    • Average Elapsed : 00:00:23 (22873 ms)
  • scaling Cluster
    • Query count : 9512
    • Average Elapsed : 00:00:07 (6850 ms)
    • first query endtime by scaling cluster : 2021/8/3 11:56:32 (laptime: 00:00:59)

Metrics - Cluster performance

Metrics - Database Performance

Metrics - Workload Concurrency

検証結果

スループット

scaling Clusterが「10」の場合、1つのMain Clusterと10のscaling Clusterのコンピューティングを合わせて、約11倍のスループットの向上が上限という仮説に基づいて検証しましたが、実際には21.5倍のスループットの向上を確認しました。この結果からscaling ClusterはMain Clusterの約2倍以上のスループットが得られていると考えられます。

  • scaling Cluster:0
    • Elapsed: 11:36:43 (43032026 ms)
    • 100% (基準値)
  • scaling Cluster:10
    • Elapsed: 00:33:18 (1997828 ms)
    • 4.6% (x21.5 Improvement)

クエリのレスポンス

今回はクエリのConcurrencyは「60」クエリと並列度を高く設定して負荷を実行しているため、scaling Clusterの設定値が小さいとQueued Queryになってレスポンスが遅くなる傾向があります。scaling Cluster「0」のクエリのレスポンスが遅い理由は、WLMのキューに積まれクエリの実行待ちが長く続いたためです。scaling Cluster「10」の場合、Main Clusterよりもscaling Clusterよって実行されたクエリは、3倍以上のレスポンスの向上が確認できました。

  • scaling Cluster:0
    • Main Cluster
      • Average Elapsed : 254752 ms
    • scaling Cluster
      • Average Elapsed : n/a
  • scaling Cluster:10
    • Main Cluster
      • Average Elapsed : 22873 ms
    • scaling Cluster
      • Average Elapsed : 6850 ms (x3.3)

スケーリング

検証用のクエリは同時60クエリで実行、Queued Queryを発生させてクラスタがスケーリング開始するまでの時間を計測したいと考えていましたが、クラスタがスケーリング開始する正確な時間が取得できないため、今回は、クエリを開始した時間からscaling Clusterで一番最初にクエリ実行が完了した時間までを計測しました。クエリの開始時間(11:55:33)とscaling Clusterで一番最初にクエリ実行が完了した時間(11:56:32)との差は、59秒でした。この差からクエリの実行時間の平均7行を引くと約52秒となります。

よって、scaling Clusterが「10」の場合は、1分未満でスケーリングクラスタが利用できることが確認できました。

  • scaling Cluster:10
    • first query starttime : 2021/8/3 11:55:33
    • first query endtime by scaling cluster : 2021/8/3 11:56:32 (laptime: 00:00:59)

コスト

scaling Cluster「10」の場合、平均9台のスケーリングクラスタが稼働していたと仮定すると、10.84USDの追加料金が発生しましたが、スループットが21.5倍に改善しました。一日のクラスタの利用費が52.128USD(=1.086(USD/hours) x 2(nodes) X 24)と比較して、約20%のコスト増加で一時的に高負荷なワークロードを処理できました。一方、scaling Cluster「0」の場合、11.5時間以上に渡りCPU使用率75%を維持しており、他のワークロードに対するレスポンスの遅延が懸念されます。

  • scaling Cluster:0
    • n/a
  • scaling Cluster:10
    • 10.84(USD) ≒ 1.086(USD/hours) x 2(nodes) X 9(clusters) X 1997.828(sec) / (60min X 60sec)

なお、scaling Clusterは、クラスタあたり1日に1時間(3600秒間)無料で利用できますので、上記の試算よりもお安くなります。

まとめ

scaling Clusterが「10」の場合、1つのMain Clusterと10のscaling Clusterのコンピューティングを合わせて、約11倍のスループットの向上が上限という仮説に基づいて検証しましたが、実際には21.5倍のスループットの向上を確認しました。今回書き込みクエリをサポートし、scaling Clusterのパフォーマンスが高く、1分未満でスケーリングできるので、よりサーバレスのような従量課金に近い利用が可能になりました。

今後は、ピークのワークロードに合わせて大きなクラスタをプロビジョニングするのではなく、Concurrency scaling(同時実行スケーリング)を有効した大きすぎないクラスタをプロビジョニングして必要に応じてスケールさせる構成がデファクトになるでしょう。既存のお客様も、Concurrency scaling(同時実行スケーリング)を用いてコスト最適化をご検討ください。

検証結果のサマリは、以下のとおりです。

  • スループットの観点では、scaling Clusterは、Main Cluster以上のスループットでワークロードを処理できる
    • scaling Clusterが「10」の場合、21.5倍のスループットの向上scaling ClusterはMain Clusterの約2倍以上のスループットが得られました。
  • パフォーマンスの観点では、scaling Clusterは、Main Cluster以上のレスポンスで結果を返すことができる
    • Main Clusterよりもscaling Clusterよって実行されたクエリは、3倍以上のレスポンスの向上が確認できました
  • 伸縮自在性の観点では、平常時の12倍のスパイクアクセスに素早くスケーリングしてクエリを処理できる
    • 1分未満でスケーリングクラスタが利用できることが確認できました
  • コストの観点では、scaling Clusterの数に応じて線形スケールするので、従来のようなクラスタをピークのワークロードにプロビジョニングすることなく、利用できるようになる
    • 本来11.5時間を要するワークロードを33分で完了、20%のコスト増加で一時的に高負荷なワークロードを処理できました
  • 検証する時間帯やタイミングでもスループットのばらつきがなく、クエリのエラーなども発生しない安定した運用が期待できます
  • 本ブログには記載していませんが、2年前と比べて参照系クエリのスループット、クラスタのスケールアウト/スケールインの速度がともに向上してしています

参考文献