【 Amazon SQS 】 フェア ( Fair ) キューを使ってみた ~ マルチテナントのノイジーネイバー問題を簡潔に解決する ~

はじめに

2025 年 10 月現在、Amazon SQS を開くとこんなバナーが目に入ります。

「SQS 公平キューが標準キューで使用できるようになりました。」

実は、Amazon SQS には 7 月に以下の機能リリースが行われているのです。

本記事では、Amazon SQS のフェアキューを実際に使ってみて、その挙動やそこから読み取れることを確認していきたいと思います。

※ 日本語の場合、「フェアキュー」「Fair キュー」「公平キュー」と表記ゆれがありますが、本記事では基本的に「フェアキュー」を使っていきます。



概要

Amazon SQS とは

Amazon Simple Queue Service(SQS)とは、アプリケーション間でメッセージの送受信を簡単に行うことができる、フルマネージドの分散メッセージキューイングサービスです。

サービス間の処理が非同期で進行可能であり、疎結合アーキテクチャの構成を容易にします。

SQS には、用途に応じて使い分ける 2 種類のキュータイプがあり、それぞれの違いは以下のようになっています。

標準キュー FIFO キュー
概要 スループットに優れている 処理の順序が保証され、また重複して処理されない
スループット ほぼ無制限 300 件/秒 をサポート
配信方式 At-least-once(少なくとも 1 回)の配信 Exactly-once(確実に 1 回)の配信
処理順序 ベストエフォート
(メッセージの処理順序は保証されず、送信された順番と異なることがある)
順序性を担保
(送信された順番にキューに残っている最も古いものからメッセージを処理する)

参照:Amazon SQS queue types

今回使っていく「フェア(Fair)キュー」は、標準キューでノイジーネイバーの影響を軽減する機能として新しく導入されました。

フェア(Fair)キューとは

Amazon SQS フェアキューは、マルチテナントにおけるノイジーネイバー問題に対応するため、導入された機能です。

マルチテナントとは

マルチテナントとは、ひとつのアプリケーションやシステムを無関係な複数のユーザー(テナント)が共有して利用するモデルのことを指します。

SaaS 型のクラウドサービスで広く採用されている方式であり、コスト削減や運用効率の改善、リソース利用効率の向上といったメリットを得ることが可能です。

AWS では、マルチテナントにおけるクロステナントアクセス(テナントが他のテナントのリソースにアクセスしてしまうこと)やノイジーネイバー(後述)などの問題を解決するためのアプローチとして「テナントの分離」を上げており、関連する複数のモデルが提唱されています。
詳しくは、AWS Well-Architected フレームワーク - SaaS レンズをご覧ください。

ノイジーネイバー問題とは

ノイジーネイバー問題とは、あるテナント(ネイバー)の過剰な共有リソース消費によって、他のテナントのサービスレベル低下を引き起こす現象のことを指します。

前述したマルチテナントのモデルの中でも、ひとつのリソースを全てのテナントで共有する「プールモデル」や、各テナント専用のリソースとテナント間で共有のリソースを組み合わせる「ブリッジモデル」において、特に発生しやすいです。

Amazon SQS の場合、あるテナントから大量のメッセージが送信されると、他のテナントからのメッセージの処理が遅くなってしまいます。

画像出典:AWS Blogs

従来はこの問題を回避するため、コンシューマーをスケーリングすることでメッセージの消費スピードを上げたり、ティアリングなどと組み合わせることで複数のキューに分散させたりする方法がとられていました。
詳しくは、AWS Blogsをご覧ください。

そしてフェアキューとは

Amazon SQS フェアキューは、上述した問題をより簡潔に解決します。

標準キューにメッセージを送信する際、メッセージグループ ID(MessageGroupId)にテナント識別子を指定するだけで、テナントごとのメッセージを自動で判別してくれるのです!

そうして特定のメッセージグループ ID をもつメッセージがキューに溜まると、フェアキューは該当のテナントをノイジーネイバー(NoisyGroups)と見なし、他のテナント(Quiet groups)に属するメッセージを優先してコンシューマーに返すようになります。

画像出典:AWS Blogs

ちなみに、Amazon SQS の FIFO キューを使用してもノイジーネイバー問題に対応することはできます。
これは、FIFO キューが各テナントからの転送中のメッセージの数を制限し、厳密な処理の順序を維持することができるためです。

しかし、FIFO キューは処理能力に上限がある一方、標準キュー(with フェアキュー)は高スループットを維持できるため、よりマルチテナントシナリオ向けであるといえます。

参照:Difference with FIFO queues

実際に使ってみる

それでは、実際に Amazon SQS フェアキューを使っていきましょう。

まず、標準キューを以下のように作成します。

(標準キューと FIFO キューに加えて)フェアキューというキュータイプが追加されたわけではありませんのでご注意ください。

次に、先ほど作成した標準キューにメッセージを送信する際、メッセージグループ ID(MessageGroupId)にテナント識別子を指定します。
(ちなみに、メッセージグループ ID は、これまで FIFO キューでしか使えませんでした)

AWS マネジメントコンソールだとこんな感じ。

AWS CLI の場合は、--message-group-id というオプションで MessageGroupId を付与します。

aws sqs send-message \
--queue-url {キューのURL} \
--message-body {メッセージ本文} \
--message-group-id {テナント識別子}

今回は、シェルスクリプトを実行することで標準キューへメッセージを送受信し、その結果を Amazon CloudWatch メトリクスや出力したログで確認していきたいと思います。

スクリプトは、簡単なものを 3 つ用意しました。


① sqs_noisy_message_send.sh
テナントN(ノイジーネイバー)のメッセージ送信を 1000 件行う

#!/bin/bash

SQS_QUEUE_NAME="blog_test"
SQS_QUEUE_URL=$(aws sqs get-queue-url \
    --queue-name ${SQS_QUEUE_NAME} \
    --output text)
DIR_SQS_MESSAGE="${HOME}/sqs_log"
FILE_SQS_MESSAGE="${DIR_SQS_MESSAGE}/${SQS_QUEUE_NAME}_send.json"
NOISY_TENANT_ID="tenant-N"

function sqs_send_message() {
    local message_body=$1
    local tenant_id=$2

    aws sqs send-message \
    --queue-url "${SQS_QUEUE_URL}" \
    --message-body "${message_body}" \
    --message-group-id "${tenant_id}"
}

echo "=== NoisyGroups メッセージ送信開始 [$(date +%H:%M:%S)] ==="

for ((i=1; i<=1000; i++))
do
    SQS_MESSAGE_BODY="[$(date +%H:%M:%S)] ${NOISY_TENANT_ID}: ${i}/1000 件"
    sqs_send_message "${SQS_MESSAGE_BODY}" "${NOISY_TENANT_ID}" >> ${FILE_SQS_MESSAGE} &

    if [ $(( i % 100 )) -eq 0 ]; then
        wait
        echo ${SQS_MESSAGE_BODY}
    fi
done

wait
echo "=== NoisyGroups メッセージ送信完了 [$(date +%H:%M:%S)] ==="

② sqs_quiet_message_send.sh
テナントA(ノイズのないテナント)のメッセージ送信を 100 件行う

#!/bin/bash

SQS_QUEUE_NAME="blog_test"
SQS_QUEUE_URL=$(aws sqs get-queue-url \
    --queue-name ${SQS_QUEUE_NAME} \
    --output text)
DIR_SQS_MESSAGE="${HOME}/sqs_log"
FILE_SQS_MESSAGE="${DIR_SQS_MESSAGE}/${SQS_QUEUE_NAME}_send.json"
QUIET_TENANT_ID="tenant-A"

function sqs_send_message() {
    local message_body=$1
    local tenant_id=$2

    aws sqs send-message \
    --queue-url "${SQS_QUEUE_URL}" \
    --message-body "${message_body}" \
    --message-group-id "${tenant_id}"
}

echo "=== QuietGroups メッセージ送信開始 [$(date +%H:%M:%S)] ==="

for ((i=1; i<=100; i++))
do
    SQS_MESSAGE_BODY="[$(date +%H:%M:%S)] ${QUIET_A_TENANT_ID}: ${i}/100 件"
    sqs_send_message "${SQS_MESSAGE_BODY}" "${QUIET_A_TENANT_ID}" >> ${FILE_SQS_MESSAGE} &

    if [ $(( i % 10 )) -eq 0 ]; then
        wait
        echo ${SQS_MESSAGE_BODY}
    fi
done

wait
echo "=== QuietGroups メッセージ送信完了 [$(date +%H:%M:%S)] ==="

③ sqs_message_receive.sh
メッセージの受信を行う

#!/bin/bash

SQS_QUEUE_NAME="blog_test"
SQS_QUEUE_URL=$(aws sqs get-queue-url \
    --queue-name ${SQS_QUEUE_NAME} \
    --output text)
DIR_SQS_MESSAGE="${HOME}/sqs_log"
FILE_SQS_MESSAGE="${DIR_SQS_MESSAGE}/${SQS_QUEUE_NAME}_receive.json"

function sqs_receive_message() {
    aws sqs receive-message \
    --queue-url "${SQS_QUEUE_URL}" \
    --attribute-names All >> ${FILE_SQS_MESSAGE}
}

echo "=== メッセージ受信開始 [$(date +%H:%M:%S)] ==="

# メッセージの受信件数は都度調整する
# 手順 2 と 5 では 200 件、手順 3 では 1000 件
for ((i=1; i<=1000; i++))
do  
    SQS_MESSAGE_BODY="[$(date +%H:%M:%S)] receive: ${i}/1000 件"
    sqs_receive_message &

    if [ $(( i % 100 )) -eq 0 ]; then
        wait
        echo "===== receive: ${i}/1000 =====" >> ${FILE_SQS_MESSAGE}
        echo ${SQS_MESSAGE_BODY}
    fi
done

wait
echo "=== メッセージ受信完了 [$(date +%H:%M:%S)] ==="

そして、以下の手順でメッセージの送受信を行いました。

手順 内容
1 ① のスクリプトを実行
2 メッセージの受信件数を 200 にして ③ のスクリプトを実行
※ この手順は、ノイジーネイバー(NoisyGroups)の判定を出すために行う
3 ② のスクリプトを実行
4 ① のスクリプトを再度実行
※ 後続の手順で大量のメッセージを受信するため、テナントN(ノイジーネイバー)のメッセージ件数を増やしておく
5 メッセージの受信件数を 200 にして ③ のスクリプトを再度実行
※ この手順は、ノイジーネイバー(NoisyGroups)の判定を出すために行う
6 メッセージの受信件数を 1000 にして ③ のスクリプトを実行

実行結果は、CloudWatch メトリクスで確認するとこのようになりました。

実は、Amazon SQS フェアキューの導入と同時に、CloudWatch にも新しい SQS メトリクスが追加されました!

ApproximateNumberOfNoisyGroups(オレンジ色の線)は、ノイジーネイバー(NoisyGroups)と判断されたテナントの数を示しています。

また、いくつかの既存のメトリクスに、ノイジーネイバーからのメッセージを除外し、ノイズのないテナント(QuietGroups)のみを対象とする、InQuietGroups というサフィックスの付与されたメトリクスが加わっています。
そのうちのひとつが、ApproximateNumberOfMessagesVisibleInQuietGroups(紫色の線)です。

画像を見ると、メッセージを受信したタイミングで、ノイジーなテナントが認識されていることが分かります。

そして、特に操作(メッセージの送受信)を行わず、メッセージ保持期間を過ぎメッセージが削除された後も、増減を繰り返しているようです。

ちなみに、ノイジーネイバー(NoisyGroups)であると判断される基準は、記事執筆現在、公式ドキュメント等に記載がないため分かりません……。

ApproximateNumberOfMessagesVisible(青色の線)と ApproximateNumberOfMessagesVisibleInQuietGroups(紫色の線)についても、もう少し詳しく見てみましょう。

あまり大きな差はありませんが、それぞれ総数に違いがあることが分かります。
AWS Blogs ではより顕著に差が出ていますので、ぜひ併せてご確認ください。

最後に、メッセージの受信順序について、出力したログを見るとざっくり以下のようになっていました。

順序 フェアキューあり フェアキューなし
1 ~ 100 テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 10 件
テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
101 ~ 200 テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 12 件
テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
201 ~ 300 テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 7 件
テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
301 ~ 400 テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
401 ~ 500 テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 2 件
テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
501 ~ 700 テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
テナントA受信: 0 件
テナントN(手順 4 で送信したもの)受信: 0 件
701 ~ 800 テナントA受信: 11 件
テナントN(手順 4 で送信したもの)受信: 0 件
テナントA受信: 30 件
テナントN(手順 4 で送信したもの)受信: 0 件
801 ~ 900 テナントA受信: 46 件
テナントN(手順 4 で送信したもの)受信: 3 件
テナントA受信: 38 件
テナントN(手順 4 で送信したもの)受信: 19 件
901 ~ 1000 テナントA受信: 22 件
テナントN(手順 4 で送信したもの)受信: 51 件
テナントA受信: 32 件
テナントN(手順 4 で送信したもの)受信: 81 件

本来は「フェアキューありの場合、テナントAを早めに受信する」を想定していたのですが、今回の検証方法だと「フェアキューありの場合(手順 4 で送信した)テナントNを早めに受信する」となっていることが分かります。

つまり、先ほど確認した CloudWatch メトリクスと合わせて考えると、テナントAこそがノイジーネイバー(NoisyGroups)であると判断されたようです!
想定とは全然違います!
(他にも様々なメッセージの送受信パターンを試してみたのですが、このパターンが一番動きを追いやすいと思い、本記事に採用させていただきました)

しかしその一方で、ノイジーネイバーが発生するとそれ以外のテナントのメッセージが優先的に返される、という挙動は確認することができましたね……。

すべて想定通りの結果を得るには、また違った方法で検証を行う必要がありそうです。

おわりに

本記事では、Amazon SQS フェア(Fair)キューについて、スクリプトを使用してメッセージの送受信を行い、またその結果を Amazon CloudWatch メトリクスやログを通して見ることで、実際の挙動やそこから読み取れることを確認していきました。

実は、Amazon SQS フェアキューの登場からすぐに、Amazon SNS 標準トピックでもこのフェアキューのサポートが開始されています。
Amazon SNS standard topics now support Amazon SQS fair queues

Amazon SNS を介して Amazon SQS を使用するというケースはかなり多いため、このアップデートは有り難いですね。
マルチテナントにおけるノイジーネイバー問題を簡潔に解決する手段のひとつとして、フェアキューを活用した構成がより現実味を帯びたと言えるでしょう。

本記事を通して、Amazon SQS フェアキューへの理解がより深まり、システムを構築する際の選択肢のひとつになればと思います。