AWS / SAP-C02

SAP-C02-088 · セクション 10 / タスク 2.5

BufferingによるPerformance安定化

Amazon SQS / Buffer / Traffic Spike / Producer / Consumer / Backpressure

システムへ届く処理要求の量は、常に一定とは限りません。

例えば通常時は、

500件 / 秒

しか届かない処理でも、キャンペーン開始や一斉通知によって短時間だけ、

5,000件 / 秒

まで急増することがあります。

このとき、処理側を常にピーク時の5,000件/秒へ合わせておく必要があるとは限りません。

非同期処理できる要求であれば、

送信側
↓
バッファ
↓
処理側

という境界を作り、

一時的な流入量
と
実際の処理速度

を分離できます。

重要なのは、

バッファを入れる
=
処理自体が速くなる

ではないことです。

バッファは、

処理要求を失わず一時的に待たせる
+
処理側が扱える速度へ平準化する

ために使います。

ゴール

次の判断ができるようにします。

一時的なトラフィックスパイク
+
非同期処理可能
+
後段を急増負荷から保護したい
        ↓
Amazon SQSによるバッファリング

さらに、

送信側の到着速度
処理側の処理速度
キューのバックログ
最古メッセージの待ち時間

から、

一時的な急増なのか

処理能力が恒常的に不足しているのか

を判断できるようにします。

1. バッファは処理速度の違いを吸収する

バッファ(Buffer)は、まだ処理されていない要求を一時的に保持する境界です。

送信側
   ↓
バッファ
   ↓
処理側

例えば、

Web API
↓
Amazon SQS
↓
画像変換ワーカー

という構成なら、Web APIと画像変換ワーカーが完全に同じ速度で動く必要はありません。

短時間だけ、

送信
2,000件 / 秒

処理
800件 / 秒

になっても、

差分
1,200件 / 秒

をキューへ蓄積できます。

2. 送信側と処理側の速度を見る

バッファリングでは、

送信側(Producer)

と、

処理側(Consumer)

の速度関係が重要です。

例えば、

送信側
1,000件 / 秒

処理側
1,000件 / 秒

なら、理論上バックログは増えません。

一方、

送信側
2,000件 / 秒

処理側
1,200件 / 秒

なら、

800件 / 秒

ずつ未処理要求が増えます。

基本関係は、

送信側の到着速度
>
処理側の処理能力
        ↓
バックログ増加

です。

言い換えると、

到着する量
>
処理できる量
        ↓
待っている仕事が増える

となります。

3. 一時的な速度差ならバッファで吸収できる

送信側が処理側より速いこと自体が、すぐ問題になるわけではありません。

例えば、

通常
送信側 500件 / 秒
処理側 1,000件 / 秒

で、キャンペーン開始直後の1分間だけ、

送信側 3,000件 / 秒
処理側 1,000件 / 秒

になったとします。

この1分間はバックログが増えます。

しかし、その後送信側が500件/秒へ戻れば、

処理側 1,000件 / 秒
-
送信側 500件 / 秒
=
毎秒500件

の速度でバックログを減らせます。

このような、

一時的なトラフィックスパイク
(Traffic Spike)

を吸収することが、バッファの代表的な役割です。

4. 恒常的な処理能力不足はバッファだけでは解決できない

一方、

送信側
2,000件 / 秒

処理側
1,500件 / 秒

という状態が何時間も続くなら、

500件 / 秒

ずつバックログが増え続けます。

Amazon SQSを間に置いたとしても、

処理能力不足

そのものは解消していません。

SQS
↓
バックログを保持できる

ことと、

処理側
↓
十分な速度で処理できる

ことは別です。

したがって、

一時的な速度差
→ バッファで吸収

恒常的な速度差
→ 処理側の増強
   処理高速化
   送信側の抑制
   などが必要

と判断します。

5. Amazon SQSを性能安定化のバッファとして使う

Amazon SQSは、完全マネージド型のメッセージキューサービスです。

性能設計では、

送信側
↓
Amazon SQS
↓
処理側

という構成によって、送信側が処理側の処理完了を待たずに要求を受け渡せることを利用します。

例えば、

注文受付API
↓
Amazon SQS
↓
帳票生成ワーカー

とします。

短時間に注文が急増しても、

注文受付
↓
SQSへ送信

までを素早く完了させ、帳票生成は、

処理側が処理可能な速度

で後から進められます。

Amazon SQSの配信保証、FIFO、可視性タイムアウト、再配信、デッドレターキューなどは、バッファによる性能平準化とは別の設計条件として扱います。

6. Amazon SQS標準キューは大規模な流入を受け止められる

Amazon SQS標準キューは、SendMessage、ReceiveMessage、DeleteMessageなどに対して非常に高い、ほぼ無制限のAPIコール数をサポートします。

そのため、

大量の送信側
↓
SQS
↓
処理側群

という高スループットのバッファとして利用できます。

ただし、

SQSがスケールする
=
処理側も無限に処理できる

ではありません。

性能ボトルネックは、

処理側
データベース
外部API

など、キューより後ろに残る可能性があります。

7. バッファはピーク負荷を平準化する

直接接続では、

トラフィックスパイク
     ↓
処理側へ
そのまま急増負荷

となります。

バッファを入れると、

トラフィックスパイク
     ↓
Amazon SQS
     ↓
処理側が処理可能な速度

にできます。

イメージすると、

流入量

  ▲
  │        ████
  │        ████
  │        ████
  │   ███████████
  └────────────────→ 時間

というピークを、

処理量

  ▲
  │      ███████████████
  │      ███████████████
  └──────────────────────→ 時間

のように時間方向へ広げます。

これが、

需要曲線を平準化する

という考え方です。

8. バッファは総処理量を減らすわけではない

100万件の処理要求が来た場合、

SQSを入れる

だけで、

100万件
↓
10万件

になるわけではありません。

処理側は最終的に100万件を処理する必要があります。

変わるのは、

いつ
どの速度で
処理するか

です。

したがってバッファは、

処理量削減

ではなく、

処理タイミングの調整

に利用します。

9. バックログを監視する

Amazon SQSでは、

ApproximateNumberOfMessagesVisible

によって、現在取得可能な未処理メッセージ数のおおよその値を確認できます。

これは、

現在どの程度
処理待ちがあるか

を見る代表的な指標です。

例えば、

10
↓
500
↓
10,000
↓
50,000

と継続的に増えているなら、

送信側の到着速度
>
処理側の処理能力

となっている可能性があります。

逆にスパイク後、

50,000
↓
30,000
↓
10,000
↓
0

と減っているなら、処理側がバックログを解消できています。

10. キューの深さだけでなく最古メッセージの待ち時間を見る

同じ10,000件のバックログでも、

1秒で処理できる10,000件

と、

30分待たされている10,000件

では性能状態が違います。

Amazon SQSでは、

ApproximateAgeOfOldestMessage

によって、最も古いメッセージのおおよその経過時間を確認できます。

例えば性能目標が、

要求をキューへ入れてから
30秒以内に処理開始

なら、

ApproximateAgeOfOldestMessage

は件数だけを見るより直接的な判断材料になります。

11. 処理待ちと処理中を分けて考える

SQSには、

ApproximateNumberOfMessagesVisible

だけでなく、

ApproximateNumberOfMessagesNotVisible

もあります。

大まかには、

ApproximateNumberOfMessagesVisible
→ まだ処理側が取得可能な処理待ち

ApproximateNumberOfMessagesNotVisible
→ 処理側が受信して処理中のメッセージ

と考えます。

例えば、

処理待ちメッセージが急増
+
処理中メッセージはほぼ一定

なら、

処理待ちだけが増えている

可能性があります。

一方、

処理中メッセージも増加

しているなら、処理側で多くのメッセージを並行処理している可能性があります。

ただし、SQSのこれらの値は分散システム上のおおよその値です。

12. 件数と待ち時間を組み合わせる

性能監視では、

ApproximateNumberOfMessagesVisible

だけを見て、

キューが多いから異常

とは限りません。

例えば、

一時スパイク
↓
処理待ち 50,000件
↓
数十秒で急速に減少

なら、設計どおりかもしれません。

一方、

処理待ち件数増加
+
ApproximateAgeOfOldestMessage増加

が続くなら、

処理側が追いついていない

可能性が高くなります。

したがって、

バックログ件数
+
バックログの待ち時間

を組み合わせて判断します。

13. 処理側をバックログに合わせてスケールする

Amazon SQSをEC2ワーカー群で処理する場合、

CPU使用率

だけを見ても適切にスケールできないことがあります。

例えば処理側が、

外部API待ち
データベースI/O待ち

を多く含むなら、

CPU 30%

でもキューが増え続ける可能性があります。

この場合は、

キューにどれだけ仕事が残っているか

をスケーリング判断へ使います。

14. バックログ / インスタンスで考える

EC2 Auto Scalingでは、SQSのバックログをワーカー数で割った、

インスタンスあたりバックログ

を利用できます。

基本式は、

ApproximateNumberOfMessagesVisible
÷
InServiceのインスタンス数

です。

例えば、

バックログ
4,000件

処理側
20台

なら、

4,000
÷
20
=
200件 / インスタンス

です。

単純なキュー全体件数より、

1台あたり
どの程度の仕事を抱えているか

を見られます。

15. 許容バックログをレイテンシーから逆算する

さらに、

許容できる待ち時間
÷
1件の平均処理時間

から、1台あたりの許容バックログを求められます。

例えば、

許容待ち時間
20秒

1件の平均処理時間
0.2秒

なら、

20
÷
0.2
=
100件

です。

つまり、

100件 / インスタンス

程度を目標値としてスケーリングを設計できます。

現在、

200件 / インスタンス

なら、

許容値100
を超えている
↓
処理側を増やす

という判断ができます。

16. 処理側を増やせばよいとは限らない

キューが増えたとき、

処理側を最大まで増やす

ことが常に正解ではありません。

例えば、

Amazon SQS
↓
Lambda
↓
RDS

で、RDSが安全に処理できる同時接続数が100なのに、

Lambda
1,000同時実行

まで増やすと、

SQSのバックログは減る

しかし

RDSが過負荷になる

可能性があります。

したがって処理側のスケーリングでは、

処理側自身の能力
+
下流システムの安全な処理能力

の両方を確認します。

17. Lambda + SQSでは同時実行数を制御できる

Amazon SQSをLambdaのイベントソースとして利用する場合、Lambdaはメッセージ量に応じて処理をスケールできます。

一方、

下流DBは100並列まで

外部APIは50並列まで

のような制約があるなら、

最大同時実行数

を使って、1つのSQSイベントソースから起動されるLambdaの同時実行数を制限できます。

この場合、

トラフィックスパイク
↓
SQSのバックログ増加
↓
処理側は安全な上限で処理

とし、下流を急激な並列実行から保護できます。

18. バックプレッシャーとは何か

バックプレッシャー(Backpressure)は、

後段が処理できない速度で
前段から要求を流し続けない

ための考え方です。

例えば、

送信側
5,000件 / 秒

処理側
1,000件 / 秒

が長時間続いているのに、

送信側
そのまま5,000件 / 秒

で投入し続ければ、バックログは増え続けます。

この場合、

一時的にはSQSで吸収

できても、最終的には、

処理側を増強

処理を高速化

送信側を抑制

新規要求の受付を制御

などが必要になります。

19. バッファとバックプレッシャーは同じではない

バッファは、

一時的に待たせる場所

です。

バックプレッシャーは、

処理能力を超えた要求が
無制限に流れ続けないようにする考え方

です。

したがって、

バッファあり
=
バックプレッシャー不要

ではありません。

例えば、

送信側
10,000件 / 秒

処理側
1,000件 / 秒

これが永続

なら、巨大なバッファがあっても根本問題は残ります。

20. バッファリングとスロットリングを使い分ける

処理能力を超えた要求への代表的な対応には、

バッファリング

と、

スロットリング(Throttling)

があります。

バッファリングは、

要求を受け付ける
↓
後で処理する

方式です。

一方、スロットリングでは、

今は受け付けられない
↓
後でもう一度試してもらう

という制御を行います。

例えば、

非同期で後から処理してよい
+
要求を一度受け付けたい
        ↓
バッファ

が有力です。

一方、

送信元が再試行できる
+
後段へこれ以上流したくない
        ↓
スロットリング

を評価できます。

21. バッファはレスポンス要件も変える

バッファを挟むと、

要求受付完了

と、

実処理完了

が別の時点になります。

例えば、

画像アップロード
↓
SQSへ受付
↓
「処理を受け付けました」
↓
後から画像変換

なら適しています。

しかし、

決済処理が成功したか確認してから
注文成功を返す

など、結果を同期的に必要とする処理では、

SQSへ入れた時点
=
業務処理成功

とはできません。

したがって、バッファリングを選ぶ前に、

非同期化できる処理か

を確認します。

22. バックログが減る速度を計算する

スパイク終了後のバックログ解消時間も重要です。

例えば、

残バックログ
200,000件

送信側
500件 / 秒

処理側
1,500件 / 秒

なら、処理側の処理能力すべてをバックログへ使えるわけではありません。

毎秒500件の新しい要求も到着するため、

1,500
-
500
=
1,000件 / 秒

が実際にバックログを減らせる速度です。

したがって、

200,000
÷
1,000
=
200秒

で解消できます。

23. 「キューがあるから大丈夫」ではない

Amazon SQSは大量のメッセージを保持できますが、バッファ設計では、

最大停止時間
+
復旧後に追いつく時間
+
許容処理遅延

を考える必要があります。

例えば、

処理側停止
↓
大量のバックログ
↓
処理側復旧

しても、

送信側と同じ速度

でしか処理できなければ、

バックログは永遠に減らない

ことになります。

復旧後には、

送信側の到着速度
<
処理側の処理能力

となる余力が必要です。

24. キャッシュとバッファを分ける

キャッシュは、

過去に取得 / 生成した結果
を再利用する

仕組みです。

バッファは、

まだ処理していない要求
を待たせる

仕組みです。

例えば、

同じ商品情報を
何度も読みたい
        ↓
キャッシュ

ですが、

注文が一時的に急増
+
後段が一度に処理できない
        ↓
バッファ

です。

25. シナリオ1:短時間だけ10倍の要求が来る

要件:

通常
500件 / 秒

キャンペーン開始時
5,000件 / 秒

処理側
1,000件 / 秒

処理は非同期可能

この場合、

送信側
↓
Amazon SQS
↓
処理側群

を評価します。

SQSで一時的な差分を保持し、

スパイク終了後
処理側がバックログを処理

できるようにします。

重要なのは、

スパイクが終わった後に
バックログを減らせる余力

があることです。

26. シナリオ2:バックログが一日中増えている

要件:

送信側
2,000件 / 秒

処理側
1,500件 / 秒

24時間継続

処理待ち件数
増加

最古メッセージの待ち時間
増加

この場合、

SQSを追加する

だけでは解決しません。

処理側の増強
+
処理時間短縮
+
送信側の抑制

などによって、

長期平均の処理能力
>=
長期平均の到着量

へ戻す必要があります。

27. シナリオ3:CPUは低いのにキューが増える

要件:

EC2処理側群

CPU
30%

SQSバックログ
増加

最古メッセージの待ち時間
増加

処理の多くは外部I/O待ち

この場合、

CPU 30%
だから
スケールアウト不要

とは判断できません。

SQSバックログ
÷
稼働中の処理側数

のように、実際の未処理仕事量に比例する指標を使ってスケーリングを評価します。

28. シナリオ4:処理側を増やすとDBが落ちる

要件:

SQS
↓
Lambda
↓
RDS

RDS安全上限
100同時処理

トラフィックスパイク時
Lambdaが急速に増える

この場合、

キューを最速で空にする

ことだけを目標にしません。

処理側の同時実行数を
下流が安全に処理できる範囲へ制限

し、

余った要求
↓
SQSで一時的に待機

させます。

これは、

バックログを許容することで
下流システムを守る

設計です。

29. 混同ポイント

混同正しい判断
SQSを入れれば処理側が高速になるSQSは処理速度そのものを上げるわけではない
キューに保持できるなら処理能力不足は解決済み恒常的に送信側 > 処理側ならバックログは増え続ける
トラフィックスパイクでは常に最大台数を常時起動する非同期処理ならバッファでピークを時間方向へ平準化できる
キューの件数だけ見ればよい最古メッセージの待ち時間も見る
CPUが低ければ処理能力は十分I/O中心の処理ではバックログ基準のスケーリングが適する場合がある
処理側は増やせるだけ増やす下流DBや外部APIの安全な処理能力も確認する
バッファとバックプレッシャーは同じバッファは待機場所、バックプレッシャーは流入を制御する考え方
バッファは永続的な処理能力不足も解決する長期平均で処理側が追いつけなければ別の対策が必要
キャッシュとバッファは同じ再利用する結果と未処理要求では目的が異なる

30. SAP-C02での判断フロー

性能問題
        ↓
要求量が一時的に急増している?
        │
        ├─ いいえ
        │    ↓
        │  恒常的な処理能力不足を評価
        │
        └─ はい
             ↓
処理を非同期化できる?
        │
        ├─ いいえ
        │    ↓
        │  同期経路のスケーリング等を評価
        │
        └─ はい
             ↓
          Amazon SQS
             ↓
          バッファリング

        ↓

送信側の到着速度
と
処理側の処理能力
を比較

        ↓

ApproximateNumberOfMessagesVisible
+
ApproximateAgeOfOldestMessage
を監視

        ↓

一時的なバックログ?
        │
        ├─ はい
        │    ↓
        │  処理側が後から解消可能か確認
        │
        └─ いいえ
             ↓
          処理側の増強
          処理高速化
          送信側の抑制
          バックプレッシャー

        ↓

処理側を増やしても
下流は安全?
        │
        ├─ はい
        │    ↓
        │  キューバックログに応じてスケール
        │
        └─ いいえ
             ↓
          処理側の同時実行数を制限
          SQSで一時待機

        ↓

性能目標を満たすか

まとめ

バッファリングで重要なのは、

Amazon SQSを使うこと

そのものではありません。

判断の中心は、

送信側
と
処理側
の処理速度差

です。

一時的に、

送信側
>
処理側

になるなら、

Amazon SQS
↓
バッファ
↓
後から処理

によってトラフィックスパイクを平準化できます。

ただし、

送信側
>
処理側

が永続するなら、

バッファだけでは解決しない

ことが重要です。

性能状態は、

ApproximateNumberOfMessagesVisible
+
ApproximateAgeOfOldestMessage

などで確認し、

処理側のスケーリング
+
下流システムの処理上限
+
バックプレッシャー

まで設計します。

最終的には、

一時的なスパイク
→ バッファ

バックログ増加
→ 処理側の能力を確認

待ち時間増加
→ スケール / 高速化

下流が限界
→ 同時実行数を制御

恒常的に到着量が多い
→ 送信側の抑制も検討

という判断ができることが目標です。

主要AWS公式資料

  • AWS Certified Solutions Architect - Professional 試験ガイド
  • Amazon SQS — 標準キュー
  • Amazon SQS — CloudWatchメトリクス
  • Amazon SQS — メッセージ処理に必要なリソース
  • Amazon EC2 Auto Scaling — Amazon SQSに基づくスケーリングポリシー
  • AWS Lambda — SQSイベントソースマッピングのスケーリング
  • AWS Well-Architected Framework — バッファリングまたはスロットリングによる需要曲線の平準化

理解確認

このページの内容を9問で確認します。

理解確認9問へ →