Queue Storage のパフォーマンスとスケーラビリティのチェックリスト
Microsoft は、Queue Storage を使用して高パフォーマンス アプリケーションを開発するための多数の実証済みプラクティスを開発してきました。 このチェックリストでは、パフォーマンスを最適化するために開発者が従うことのできる主要なプラクティスを示します。 アプリケーションを設計している間、およびプロセス全体を通して、これらのプラクティスに留意してください。
Azure Storage には、容量、トランザクション レート、および帯域幅についてスケーラビリティとパフォーマンスのターゲットがあります。 Azure Storage のスケーラビリティ ターゲットの詳細については、「Standard Storage アカウントのスケーラビリティとパフォーマンスのターゲット」および「Queue Storage のスケーラビリティとパフォーマンスのターゲット」を参照してください。
チェック リスト
この記事では、パフォーマンスに関する実証済みプラクティスを、Queue Storage アプリケーションの開発中に従うことのできるチェックリストにまとめています。
スケーラビリティ ターゲット
アプリケーションがいずれかのスケーラビリティ ターゲットに近づいたり超過したりすると、トランザクション待機時間や調整が増加することがあります。 Azure Storage によってアプリケーションが調整されると、サービスが 503 (Server Busy
) または 500 (Operation Timeout
) のエラー コードを返し始めます。 スケーラビリティ ターゲットの制限内にとどまることでこれらのエラーを回避することは、アプリケーションのパフォーマンスを強化するうえで重要な部分です。
Queue Storage のスケーラビリティ ターゲットの詳細については、Azure Storage のスケーラビリティおよびパフォーマンスのターゲットに関するセクションを参照してください。
ストレージ アカウントの最大数
特定のサブスクリプションとリージョンの組み合わせについて許容されるストレージ アカウントの最大数に近づいてきた場合に、複数のストレージ アカウントを使用したシャード化により、イングレス、エグレス、1 秒あたりの I/O 操作 (IOPS)、または容量を増やすことがあります。 このシナリオでは、ワークロードに必要なストレージ アカウントの数を減らすために、可能であればストレージ アカウントの制限を引き上げることをお勧めします。 Azure サポートに連絡して、ストレージ アカウントの制限の引き上げをご依頼ください。
容量とトランザクションのターゲット
アプリケーションが 1 つのストレージ アカウントに対するスケーラビリティ ターゲットに接近している場合は、次の方法のいずれかを検討し、適用します。
- キューのスケーラビリティ ターゲットがアプリケーションにとって十分でない場合は、複数のキューを使用してメッセージを分散させる必要があります。
- 対象のアプリケーションでスケーラビリティ ターゲットに対する接近や超過を引き起こしたワークロードを見直します。 設計を変更して、必要な帯域幅や処理能力を抑えたり、トランザクションを減らしたりすることができないでしょうか?
- アプリケーションがいずれかのスケーラビリティ ターゲットを超過することがほぼ確実な場合には、複数のストレージ アカウントを作成し、それらのアカウントにアプリケーション データを分けて配置します。 このパターンを使用する場合は、後で負荷分散用のストレージ アカウントを追加できるようにアプリケーションを設計してください。 ストレージ アカウント自体では、データ保存、トランザクション実行、データ転送以外の使用に料金が発生することはありません。
- アプリケーションが帯域幅ターゲットに近づいてきた場合は、クライアント側でデータを圧縮し、Azure Storage へのデータ送信に必要な帯域幅を削減する方法を検討します。 データを圧縮することにより帯域幅の節約とネットワーク パフォーマンスの改善が期待できますが、パフォーマンスにマイナスの影響が及ぶ可能性もあります。 クライアント側でデータの圧縮と展開の処理要件が増加することにより生じるパフォーマンスへの影響を評価してください。 圧縮データを格納すると、標準ツールを使用してデータが見づらくなるため、トラブルシューティングが困難になる場合があることに留意してください。
- アプリケーションがスケーラビリティ ターゲットに近づいている場合は、再試行にエクスポネンシャル バックオフを使用していることを確認してください。 この記事に書かれている推奨事項を実践して、スケーラビリティ ターゲットへの到達を回避することを強くお勧めします。 ただし、再試行にエクスポネンシャル バックオフを使用するとアプリケーションの迅速な再試行が妨げられ、調整が悪化する可能性もあります。 詳細については、「タイムアウト エラーとサーバー ビジー エラー」セクションを参照してください。
ネットワーク
アプリケーションの物理ネットワークの制約がパフォーマンスに大きな影響を及ぼすことがあります。 以降のセクションでは、ユーザーが遭遇する可能性のあるいくつかの制限について説明します。
クライアントのネットワーク機能
ネットワーク リンクの帯域幅と接続品質は、アプリケーションのパフォーマンスに重要な役割を果たします。以降のセクションでは、この点について説明しています。
スループット
帯域幅については、多くの場合にクライアントの処理能力が問題になります。 大きい Azure インスタンスは、処理能力の高い NIC を使用します。そのため、1 台のコンピューターのネットワーク制限を引き上げる必要がある場合は、大きなインスタンスを使用するか VM の数を増やすことを検討してください。 オンプレミスのアプリケーションから Azure Storage にアクセスする場合にも、同じ規則が当てはまります。クライアント デバイスのネットワーク性能と、Azure Storage の場所へのネットワーク接続を把握し、それらを必要に応じて増強するか、それぞれの性能の範囲内でアプリケーションが稼働するように設計してください。
接続品質
他のネットワーク運用と同様に、エラーやパケット損失が生じるネットワーク状態では、遅延が生じて有効なスループットが損なわれることに留意してください。 Wireshark またはネットワーク モニターを使用すると、この問題の診断に役に立つ可能性があります。
場所
分散型環境では、サーバーの近くにクライアントを配置すると、パフォーマンスが最大になります。 最小限の遅延で Azure Storage にアクセスするには、同じ Azure リージョン内にクライアントを配置するのが最適です。 たとえば、Azure Storage を使用する Azure Web アプリを 1 つ保有している場合は、その両方を単一のリージョン内に配置します (米国西部や東南アジア)。 リソースを併置することにより待ち時間が短縮され、コストが低下します。1 つのリージョン内での帯域幅使用は無料であるためです。
Azure 内にホストされていないクライアント アプリケーション (モバイル デバイス アプリやオンプレミスのエンタープライズ サービスなど) が Azure Storage にアクセスする場合、それらのクライアントに近いリージョンにストレージ アカウントを配置することで待機時間が短くなる可能性があります。 クライアントが広範囲に分散されている場合 (一部が北米に、一部がヨーロッパに存在する場合など) は、ストレージ アカウントをリージョンごとに 1 つ使用することを検討します。 アプリケーションが保存するデータが個々のユーザーに固有であり、ストレージ アカウント間でデータをレプリケートする必要がなければ、これは導入しやすい方法です。
SAS と CORS
ユーザーの Web ブラウザーや携帯電話アプリで実行されている JavaScript などのコードが Azure Storage 内のデータにアクセスするのを承認する必要があるとします。 1 つの方法として、プロキシとして動作するサービス アプリケーションを作成することが考えられます。 このサービスに対してユーザーのデバイスが認証を行うと、Azure Storage リソースへのアクセスがそのサービスによって承認されるというものです。 この方法では、安全でないデバイスにストレージ アカウント キーを知らせずに済みます。 しかし、この方法では、サービス アプリケーションに大きなオーバーヘッドが生じます。ユーザーのデバイスと Azure Storage との間で転送されるデータがすべてそのサービス アプリケーションを通過することになるためです。
Shared Access Signature (SAS) を使用すると、サービス アプリケーションを Azure Storage のプロキシとして用いることを回避できます。 SAS を使用すれば、ユーザーのデバイスから制限付きアクセス トークンを使って、Azure Storage に直接要求を実行できるようになります。 たとえば、ユーザーがアプリケーションに写真をアップロードしたい場合に、サービス アプリケーションで SAS を生成してユーザーのデバイスに送信することが考えられます。 Azure Storage リソースへの書き込みアクセス許可を SAS トークンで与えることが可能です。アクセス許可には期間が指定され、その期間を過ぎると SAS トークンの有効期限が切れます。 SAS の詳細については、「Shared Access Signatures (SAS) を使用して Azure Storage リソースへの制限付きアクセスを許可する」を参照してください。
通常、あるドメイン上の Web サイトでホストされているページの JavaScript が、別のドメインに対して特定の操作 (書き込みなど) を実行することは、Web ブラウザーによって許可されません。 このポリシーは "同一オリジン ポリシー" と呼ばれ、ページ上の悪意のあるスクリプトが別の Web ページ上のデータにアクセスすることを阻止するものです。 ただし、クラウドのソリューションを構築するときには、同一オリジン ポリシーが制限になることがあります。 クロスオリジン リソース共有 (CORS) はブラウザーの機能です。ソース ドメインで生成された要求が信頼済みであることをターゲット ドメインがブラウザーに伝達できます。
たとえば、Azure で実行されている Web アプリケーションが Azure Storage アカウントにリソースを要求するとします。 Web アプリケーションがソース ドメインで、ストレージ アカウントがターゲット ドメインです。 任意の Azure Storage サービスに対して CORS を構成して、ソース ドメインからの要求が Azure Storage によって信頼されていることを Web ブラウザーに伝えることができます。 CORS の詳細については、「Azure Storage でのクロスオリジン リソース共有 (CORS) のサポート」を参照してください。
SAS と CORS はどちらも、Web アプリケーションに対する不要な負荷をなくす効果があります。
.NET 構成
このセクションでは、.NET Framework を使用するプロジェクトの場合に、パフォーマンスの大幅な向上を図るために利用できるいくつかの簡単な構成設定を示します。 .NET 以外の言語を使用している場合は、その言語に類似の概念がないか確認してください。
既定の接続数の上限を引き上げる
Note
接続プールは ServicePointManager クラスによって制御されるため、このセクションは .NET Framework を使用するプロジェクトに適用されます。 .NET Core では、接続プールの管理に関して大幅な変更が導入されました。接続プールは HttpClient レベルで行われ、既定ではプールのサイズが制限されません。 つまり、HTTP 接続はワークロードを満たすように自動的にスケーリングされます。 パフォーマンスの向上を活用するには、可能な場合は最新バージョンの .NET を使用することをお勧めします。
.NET Framework を使用するプロジェクトでは、次のコードを使用して、既定の接続数の上限 (通常、クライアント環境では 2、サーバー環境では 10) を 100 に引き上げることができます。 一般的に、この値はアプリケーションが使用するおおよそのスレッド数に設定します。 接続数の上限は、接続を開始する前に設定してください。
ServicePointManager.DefaultConnectionLimit = 100; //(Or More)
.NET Framework での接続プールの制限について詳しくは、「.NET Framework での接続プールの制限と .NET 用の新しい Azure SDK」をご覧ください。
他のプログラミング言語については、ドキュメントを参照して接続数の上限の設定方法を確認してください。
スレッドの最小数を増やす
同期呼び出しを非同期タスクと共に使用している場合、スレッド プールのスレッド数を増やしたい場合があります。
ThreadPool.SetMinThreads(100,100); //(Determine the right number for your application)
詳細については、ThreadPool.SetMinThreads メソッドを参照してください。
無制限の並列処理
並列処理はパフォーマンスの観点では非常に有用ですが、無制限の並列処理を使用すると、スレッド数や並列要求数に対して適用される制限がなくなることになるので、注意が必要です。 同じストレージ アカウント内の複数のパーティションにアクセスする状況や、同じパーティション内の複数の項目にアクセスする状況では、データをアップロードまたはダウンロードするための並列要求の数を制限するようにしてください。 並列処理が無制限の場合、アプリケーションはクライアント デバイスの処理能力やストレージ アカウントのスケーラビリティ ターゲットを超過することがあり、その結果、待ち時間や調整時間が長くなります。
クライアント ライブラリとツール
パフォーマンスを最大限に引き出すためには必ず、Microsoft から提供される最新のクライアント ライブラリとツールを使用してください。 Azure Storage のクライアント ライブラリは、さまざまな言語に対応しています。 また、Azure Storage は PowerShell と Azure CLI をサポートします。 Microsoft はパフォーマンスに留意してこれらのクライアント ライブラリとツールを積極的に開発し、最新のサービス バージョンに遅れることなく対応して、数多くのパフォーマンスの実証済みプラクティスを内部で確実に処理できるように取り組んでいます。 詳細については、Azure Storage のリファレンス ドキュメントを参照してください。
サービス エラーの処理
サービスが要求を処理できない場合、Azure Storage からエラーが返されます。 特定のシナリオで Azure Storage から返される可能性のあるエラーについての知識は、パフォーマンスを最適化するうえで役立ちます。
タイムアウト エラーとサーバー ビジー エラー
アプリケーションがスケーラビリティの限界に近づくと、アプリケーションに対して Azure Storage による調整が発生することがあります。 場合によっては、なんらかの一時的な状態によって、Azure Storage が要求を処理できなくなることもあります。 どちらのケースでも、サービスからは 503 (Server Busy
) または 500 (Timeout
) エラーが返される可能性があります。 スループットを高めるためにデータ パーティションがサービスによって再調整されている場合にも、これらのエラーが発生することがあります。 通常、クライアント アプリケーションは、そうしたエラーを引き起こしている操作を再試行する必要があります。 ただし、スケーラビリティ ターゲットを超過しているためにアプリケーションに Azure Storage による調整が発生している場合や、他のなんらかの理由でサービスが要求を処理できない場合、積極的に再試行を実行すると問題が悪化することがあります。 再試行ポリシーにはエクスポネンシャル バックオフを使用することをお勧めします。エクスポネンシャル バックオフは、クライアント ライブラリの既定の動作にもなっています。 たとえば、アプリケーションが 2 秒後、次に 4 秒後、次に 10 秒後、次に 30 秒後に再試行し、その後再試行を断念することがあります。 そうすれば、調整が起こって動作が悪化することなく、サービスに対するアプリケーションの負荷を大幅に軽減できます。
接続エラーは、調整の結果ではなく、一時的な問題と予想されるので、直後に再試行を実行してかまいません。
再試行できないエラー
クライアント ライブラリは、再試行できるエラーとできないエラーを認識して再試行を処理します。 ただし、Azure Storage REST API を直接呼び出している場合は、再試行すべきではないエラーも一部存在します。 たとえば、400 (Bad Request
) エラーは、クライアント アプリケーションから送信された要求が想定外の形式であったために処理できなかったことを示しています。 この要求を再送信しても、毎回同じ応答が返されることになるので、再試行は無意味です。 Azure Storage REST API を直接呼び出している場合は、どのようなエラーが生じる可能性があるか、また、それらを再試行すべきかどうかを意識するようにしてください。
Azure Storage のエラー コードの詳細については、「状態コードとエラー コード」を参照してください。
Nagle のアルゴリズムの無効化
Nagle のアルゴリズムは、ネットワーク パフォーマンスを向上させる方法として、TCP/IP ネットワークで広く使用されています。 ただし、すべての環境 (高度な対話形式の環境など) で最適であるとは言えません。 Nagle のアルゴリズムは Azure Table Storage に対する要求のパフォーマンスにマイナスの影響を及ぼすため、可能な場合は無効にしてください。
メッセージ サイズ
メッセージ サイズが増加すると、キューのパフォーマンスとスケーラビリティが低下します。 メッセージには、受信者が必要な情報のみを含めてください。
一括取得
1 回の操作でキューから最大 32 個のメッセージを取得できます。 一括取得により、クライアント アプリケーションとのラウンド トリップの数を減らすことができます。これは、モバイル デバイスなどの待ち時間が長い環境では特に有用です。
キューのポーリング間隔
多くのアプリケーションはキューからメッセージをポーリングします。キューは、そのアプリケーションにとって最大のトランザクション ソースの 1 つです。 ポーリング間隔を適切に選択します。ポーリング頻度が高すぎると、アプリケーションはキューのスケーラビリティ ターゲットに近づく可能性があります。 ただし、200,000 トランザクションあたり 0.01 ドル (執筆時点) で、1 つのプロセッサが 1 か月間 1 秒に 1 回ポーリングする場合、そのコストは 15 セントにも満たないため、通常、コストはポーリング間隔の選択に影響を与える要因ではありません。
最新のコストの情報については、Azure Storage の価格に関するページを参照してください。
メッセージ更新操作の実行
メッセージ更新操作を実行すると、非表示タイムアウトを長くしたり、メッセージの状態情報を更新したりすることができます。 ジョブの各ステップが完了するごとに次のキューにジョブを渡すワークフローよりも、このアプローチの方が効率的である場合があります。 アプリケーションは、ジョブの各ステップが完了するたびに次のステップのメッセージを再キューイングするのではなく、ジョブ状態をメッセージに保存して、処理を継続することができます。 各メッセージ更新操作はスケーラビリティ ターゲットにカウントされることに留意してください。
アプリケーションのアーキテクチャ
アプリケーション アーキテクチャのスケーラビリティを確保するには、キューを使用します。 以下のリストは、キューを使用して、アプリケーションの拡張性を高めるための方法を示しています。
- キューを使用して、処理に関する作業のバックログを作成し、アプリケーションのワークロードを平滑化するために利用できます。 たとえば、アップロード済み画像のサイズ変更など、プロセッサの負荷が高い作業の要求をキューに残しておくことができます。
- キューを使用して、アプリケーションの一部を切り離し、個別に拡張することができます。 たとえば、Web フロントエンドがユーザーから得られた調査結果をキューに配置し、将来の解析とストレージに活用できます。 必要に応じて、キュー データを処理する worker ロール インスタンスを追加できます。