Important
これは Azure Cosmos DB 用の最新の Java SDK では "ありません"。 プロジェクトを Azure Cosmos DB Java SDK v4 にアップグレードしてから、Azure Cosmos DB Java SDK v4 のパフォーマンスに関するヒント ガイドをお読みください。 アップグレードするには、 Azure Cosmos DB Java SDK v4 への移行 ガイドと Reactor vs RxJava ガイドの手順に従ってください。
この記事のパフォーマンスに関するヒントは、Azure Cosmos DB Async Java SDK v2 のみを対象としています。 詳細については、Azure Cosmos DB Async Java SDK v2 リリース ノート、 Maven リポジトリ、および Azure Cosmos DB Async Java SDK v2 トラブルシューティング ガイドを 参照してください。
Important
2024 年 8 月 31 日に、Azure Cosmos DB Async Java SDK v2.x は廃止されます。SDK と SDK を使用するすべてのアプリケーション は引き続き機能します。Azure Cosmos DB では、この SDK の追加のメンテナンスとサポートが提供されなくなります。 上記の手順に従って Azure Cosmos DB Java SDK v4 に移行することをお勧めします。
Azure Cosmos DB は、高速で柔軟性に優れた分散データベースです。待機時間とスループットが保証されており、シームレスにスケーリングできます。 Azure Cosmos DB でデータベースをスケーリングするために、アーキテクチャを大きく変更したり、複雑なコードを記述したりする必要はありません。 スケールアップとスケールダウンは、API 呼び出しか SDK メソッド呼び出しを 1 回行うだけで簡単に実行できます。 ただし、Azure Cosmos DB はネットワーク呼び出しを介してアクセスされるため、 Azure Cosmos DB Async Java SDK v2 を使用する場合にピーク パフォーマンスを実現するために実行できるクライアント側の最適化があります。
データベースのパフォーマンスを向上させる場合は、以下のオプションを検討してください。
ネットワーク
接続モード: 直接モードを使用する
クライアントが Azure Cosmos DB に接続する方法は、特にクライアント側の待機時間の観点から、パフォーマンスに重要な影響を与えます。 ConnectionMode は、クライアント ConnectionPolicy を構成するために使用できるキー構成設定です。 Azure Cosmos DB Async Java SDK v2 の場合、使用可能な 2 つの ConnectionMode は次のとおりです。
ゲートウェイ モードはすべての SDK プラットフォームでサポートされており、既定で構成されているオプションです。 ファイアウォールの制限が厳しい企業ネットワーク内でアプリケーションを実行する場合、ゲートウェイ モードは標準の HTTPS ポートと単一のエンドポイントを使用するため、最適な選択肢です。 ただし、パフォーマンスのトレードオフは、データが Azure Azure Cosmos DB に読み取りまたは書き込まれるたびに、ゲートウェイ モードに追加のネットワーク ホップが含まれることです。 このため、ダイレクト モードでは、ネットワーク ホップが少なくなるため、パフォーマンスが向上します。
ConnectionMode は、ConnectionPolicy パラメーターを使用して DocumentClient インスタンスの構築中に構成されます。
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
public ConnectionPolicy getConnectionPolicy() {
ConnectionPolicy policy = new ConnectionPolicy();
policy.setConnectionMode(ConnectionMode.Direct);
policy.setMaxPoolSize(1000);
return policy;
}
ConnectionPolicy connectionPolicy = new ConnectionPolicy();
DocumentClient client = new DocumentClient(HOST, MASTER_KEY, connectionPolicy, null);
パフォーマンスを確保するために同じ Azure リージョン内にクライアントを併置する
可能であれば、Azure Cosmos DB を呼び出すアプリケーションを Azure Cosmos DB データベースと同じリージョンに配置します。 おおよその比較では、Azure Cosmos DB の呼び出しは、同じリージョン内であれば 1 から 2 ミリ秒以内で完了するのに対し、米国西部と米国東部の間では待ち時間が 50 ミリ秒より長くなります。 要求がクライアントから Azure データセンターの境界まで流れるときに使用されるルートに応じて、この待機時間が要求ごとに異なる可能性があります。 最短の待機時間は、プロビジョニングされた Azure Cosmos DB エンドポイントと同じ Azure リージョン内に呼び出し元アプリケーションを配置することによって実現されます。 使用可能なリージョンの一覧については、「 Azure のリージョン」を参照してください。
SDK の使用状況
最新の SDK をインストールする
Azure Cosmos DB SDK は、最適なパフォーマンスを提供するために頻繁に改善されています。 最新の SDK を確認し、改善点を確認するには、Azure Cosmos DB Async Java SDK v2 リリース ノート のページを参照してください。
アプリケーションの有効期間中はシングルトン Azure Cosmos DB クライアントを使用する
各 AsyncDocumentClient インスタンスはスレッド セーフであり、効率的な接続管理とアドレス キャッシュを実行します。 AsyncDocumentClient による効率的な接続管理とパフォーマンスの向上を実現するには、アプリケーションの有効期間中、AppDomain ごとに AsyncDocumentClient の単一インスタンスを使用することをお勧めします。
ConnectionPolicy のチューニング
既定では、Azure Cosmos DB Async Java SDK v2 を使用する場合、ダイレクト モードの Azure Cosmos DB 要求は TCP 経由で行われます。 内部的には、SDK は特別なダイレクト モード アーキテクチャを使用して、ネットワーク リソースを動的に管理し、最高のパフォーマンスを得ます。
Azure Cosmos DB Async Java SDK v2 では、ほとんどのワークロードでデータベースのパフォーマンスを向上させるには、ダイレクト モードが最適な選択肢です。
- ダイレクト モードの概要
ダイレクト モードで使用されるクライアント側アーキテクチャにより、予測可能なネットワーク使用率と Azure Cosmos DB レプリカへの多重化アクセスが可能になります。 上の図は、Direct モードがクライアント要求を Azure Cosmos DB バックエンドのレプリカにルーティングする方法を示しています。 ダイレクト モード アーキテクチャでは、DB レプリカごとにクライアント側に最大 10 個のチャネル が割り当てられます。 チャネルは、30個の要求が蓄えられた要求バッファーの後に続くTCP接続です。 レプリカに属するチャネルは、レプリカの サービス エンドポイントによって必要に応じて動的に割り当てられます。 ユーザーがダイレクト モードで要求を発行すると、 TransportClient は パーティション キーに基づいて適切なサービス エンドポイントに要求をルーティングします。 要求キューは、サービス エンドポイントの前に要求をバッファーします。
Direct モードの ConnectionPolicy 構成オプション
最初の手順として、次の推奨構成設定を使用します。 この特定のトピックで問題が発生した場合は、 Azure Cosmos DB チーム にお問い合わせください。
参照データベースとして Azure Cosmos DB を使用している場合 (つまり、データベースは多くのポイント読み取り操作と少数の書き込み操作に使用されます)、 idleEndpointTimeout を 0 (つまりタイムアウトなし) に設定してもかまいません。
構成オプション 既定値 bufferPageSize 8192 connectionTimeout "PT1M" アイドルチャネルタイムアウト "PT0S" idleEndpointTimeout "PT1M10S" 最大バッファ容量 8388608 エンドポイントごとの最大チャネル数 10 maxRequestsPerChannel(チャンネルごとの最大リクエスト数) 30 受信ハング検出時間 "PT1M5S" requestExpiryInterval "PT5S" リクエストタイムアウト "PT1M" requestTimerResolution "PT0.5S" sendHangDetectionTime "PT10S" shutdownTimeout "PT15S"
ダイレクト モードのプログラミングに関するヒント
SDK の問題を解決するためのベースラインとして、Azure Cosmos DB Async Java SDK v2 のトラブルシューティング に関する記事を確認してください。
ダイレクト モードを使用する場合の重要なプログラミングのヒントを次に示します。
アプリケーションでマルチスレッドを使用して効率的な TCP データ転送 を行う - 要求を行った後、アプリケーションは別のスレッドでデータを受信するようにサブスクライブする必要があります。 これを行わないと、意図しない "半二重" 操作が強制され、後続の要求は前の要求の応答を待機してブロックされます。
専用スレッドでコンピューティング集中型のワークロードを実行 する - 前のヒントと同様の理由から、複雑なデータ処理などの操作は別のスレッドに配置することをお勧めします。 別のデータ ストアからデータを取り込む要求 (たとえば、スレッドが Azure Cosmos DB と Spark データ ストアを同時に利用している場合) には待機時間が長くなる可能性があり、他のデータ ストアからの応答を待機する追加のスレッドを生成することをお勧めします。
- Azure Cosmos DB Async Java SDK v2 の基になるネットワーク IO は、Netty によって管理されます。 Netty IO スレッドをブロックするコーディング パターンを回避するためのヒントを参照してください。
データ モデリング - Azure Cosmos DB SLA では、ドキュメント サイズが 1 KB 未満であると想定されています。 データ モデルとプログラミングを最適化してドキュメント サイズを小さくすると、一般に待機時間が短縮されます。 1 KB を超えるドキュメントのストレージと取得が必要になる場合は、ドキュメントが Azure Blob Storage 内のデータにリンクされるようにすることをお勧めします。
パーティション分割コレクションの並列クエリのチューニング
Azure Cosmos DB Async Java SDK v2 では、並列クエリがサポートされており、パーティション分割されたコレクションに対して並列クエリを実行できます。 詳細については、SDK の操作に関連する コード サンプル を参照してください。 並列クエリは、対応するシリアルクエリよりもクエリの待機時間とスループットを向上するように設計されています。
setMaxDegreeOfParallelism のチューニング:
並列クエリは、複数のパーティションに並列にクエリを実行することによって機能します。 個別のパーティション分割コレクションのデータは、クエリに従って順次フェッチされます。 そのため、setMaxDegreeOfParallelism を使用して、他のすべてのシステム条件が同じままである場合に、最もパフォーマンスの高いクエリを達成する可能性が最大のパーティション数を設定します。 パーティションの数がわからない場合は、setMaxDegreeOfParallelism を使用して高い数値を設定できます。システムは並列処理の最大限度として最小 (パーティション数、ユーザー指定入力) を選択します。
クエリに関してデータがすべてのパーティションに均等に分散されている場合は、並列クエリが最適な利点を生み出すことに注意してください。 パーティション分割されたコレクションが、クエリによって返されるデータのすべてまたは大部分が少数のパーティション (最悪の場合は 1 つのパーティション) に集中するような方法でパーティション分割されている場合、それらのパーティションによってクエリのパフォーマンスがボトルネックになります。
setMaxBufferedItemCount の調整:
並列クエリは、結果の現在のバッチがクライアントによって処理されている間に結果をプリフェッチするように設計されています。 プリフェッチは、クエリの全体的な待機時間の向上に役立ちます。 setMaxBufferedItemCount はプリフェッチされた結果の数を制限します。 setMaxBufferedItemCount を返される予想される結果の数 (またはそれ以上) に設定すると、クエリはプリフェッチによる最大のメリットを受けることができます。
プリフェッチは、MaxDegreeOfParallelism に関係なく同じように機能し、すべてのパーティションのデータに対して 1 つのバッファーがあります。
getRetryAfterInMilliseconds 間隔でバックオフを実装する
パフォーマンス テスト中は、要求のレートが小さくなるまで負荷を増やす必要があります。 スロットルされた場合、クライアントアプリケーションはそのサーバーが指定する再試行間隔に応じて待機する必要があります。 バックオフを尊重することで、再試行の間に待機する時間を最小限に抑えることができます。
クライアント ワークロードをスケールアウトする
高スループット レベル (>50,000 RU/秒) でテストする場合、CPU またはネットワーク使用率でマシンが上限に達したため、クライアント アプリケーションがボトルネックになる可能性があります。 この状態に達しても、クライアント アプリケーションを複数のサーバーにスケールアウトすることで引き続き同じ Azure Cosmos DB アカウントで対応できます。
名前ベースのアドレス指定を使用する
名前ベースのアドレス指定を使用します。リンクの形式は SelfLinks (_self) ではなく、
dbs/MyDatabaseId/colls/MyCollectionId/docs/MyDocumentIdで、リンクの構築に使用されるすべてのリソースの ResourceId を取得しないようにdbs/<database_rid>/colls/<collection_rid>/docs/<document_rid>形式になります。 また、これらのリソースは再作成されるため (同じ名前の場合もあります)、キャッシュしても役に立たない可能性があります。パフォーマンスを向上させるために、クエリ/読み取りフィードのページ サイズを調整する
読み取りフィード機能 (readDocuments など) を使用してドキュメントの一括読み取りを実行する場合、または SQL クエリを発行するときに、結果セットが大きすぎる場合は、セグメント化された方法で結果が返されます。 既定では、結果が 100 項目または 1 MB に達した時点で単位として返されます。どちらの上限が最初に達するかによります。
該当するすべての結果を取得するために必要なネットワーク ラウンド トリップの数を減らすために、 x-ms-max-item-count 要求ヘッダーを使用してページ サイズを最大 1000 に増やすことができます。 一部の結果のみを表示する必要がある場合 (たとえば、ユーザー インターフェイスまたはアプリケーション API が一度に 10 件の結果のみを返す場合)、ページ サイズを 10 に減らして、読み取りとクエリに使用されるスループットを減らすこともできます。
setMaxItemCount メソッドを使用してページ サイズを設定することもできます。
適切なスケジューラを使用する (イベント ループの IO Netty スレッドを盗まない)
Azure Cosmos DB Async Java SDK v2 は、非ブロッキング IO に netty を使用します。 SDK は、固定数の IO netty イベント ループ スレッド (コンピューターの CPU コアと同じ数) を使って IO 操作を実行します。 API によって返される Observable は、共有 IO イベント ループ netty スレッドのいずれかで結果を出力します。 したがって、共有 IO イベント ループ netty スレッドをブロックしないことが重要です。 IO イベント ループ netty スレッドで CPU 負荷の高い作業またはブロック操作を実行すると、デッドロックが発生したり、SDK のスループットが大幅に低下したりする可能性があります。
たとえば、次のコードは、イベント ループ IO netty スレッドで CPU 負荷の高い作業を実行します。
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument( collectionLink, document, null, true); createDocObs.subscribe( resourceResponse -> { //this is executed on eventloop IO netty thread. //the eventloop thread is shared and is meant to return back quickly. // // DON'T do this on eventloop IO netty thread. veryCpuIntensiveWork(); });結果に対して CPU を集中的に使用する作業を行う場合は、結果を受信した後、イベント ループ IO netty スレッドで行わないようにする必要があります。 代わりに、独自の Scheduler を指定して、作業を実行するための独自のスレッドを提供できます。
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
import rx.schedulers; Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument( collectionLink, document, null, true); createDocObs.subscribeOn(Schedulers.computation()) subscribe( resourceResponse -> { // this is executed on threads provided by Scheduler.computation() // Schedulers.computation() should be used only when: // 1. The work is cpu intensive // 2. You are not doing blocking IO, thread sleep, etc. in this thread against other resources. veryCpuIntensiveWork(); });作業の種類に基づいて、作業に適切な既存の RxJava Scheduler を使用する必要があります。 こちら
Schedulersをお読みください。詳細については、Azure Cosmos DB Async Java SDK v2 の GitHub ページ を参照してください。
netty のログを無効にする
Netty ライブラリのログ記録は冗長であり、追加の CPU コストを回避するためにオフにする必要があります (設定でログを抑制するだけでは不十分な場合があります)。 デバッグ モードではない場合は、netty のログを完全に無効にします。 したがって、log4j を使用して、
org.apache.log4j.Category.callAppenders()によって発生する追加の CPU コストを netty から削除する場合は、次の行をコードベースに追加します。org.apache.log4j.Logger.getLogger("io.netty").setLevel(org.apache.log4j.Level.OFF);OS の開かれるファイルのリソース制限
Red Hat などの一部の Linux システムには、開かれるファイルの数、したがって合計接続数に上限があります。 現在の制限を確認するには、次のコマンドを実行します。
ulimit -a開いているファイル (nofile) の数は、OS によって構成された接続プールのサイズやその他の開いているファイルに十分なスペースを持つのに十分な大きさである必要があります。 大きい接続プール サイズに対応できるように変更できます。
limits.conf ファイルを開きます。
vim /etc/security/limits.conf次の行を追加または変更します。
* - nofile 100000
インデックス作成ポリシー
インデックス作成から未使用のパスを除外して書き込みを高速化する
Azure Cosmos DB のインデックス作成ポリシーでは、インデックス作成パス (setIncludedPaths と setExcludedPaths) を使って、インデックス作成に含めたり除外したりするドキュメント パスを指定できます。 インデックス作成コストはインデックス付きの一意のパスの数に直接関係するため、パスのインデックス作成を使用すると、クエリ パターンが事前にわかっているシナリオで書き込みパフォーマンスが向上し、インデックス ストレージを削減できます。 たとえば、次のコードは、"*" ワイルドカードを使用して、ドキュメントのセクション全体 (サブツリーとも呼ばれます) をインデックス作成から除外する方法を示しています。
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
Index numberIndex = Index.Range(DataType.Number); numberIndex.set("precision", -1); indexes.add(numberIndex); includedPath.setIndexes(indexes); includedPaths.add(includedPath); indexingPolicy.setIncludedPaths(includedPaths); collectionDefinition.setIndexingPolicy(indexingPolicy);詳細については、Azure Cosmos DB インデックス作成ポリシーに関するページをご覧ください。
スループット
測定と調整によって 1 秒あたりの要求ユニットの使用量を削減する
Azure Cosmos DB には、UDF、ストアド プロシージャ、トリガーを使ったリレーショナル クエリや階層クエリなど、さまざまなデータベース操作が用意されています。これらの操作はすべて、データベース コレクション内のドキュメントに対して実行できます。 これらの操作のそれぞれに関連付けられたコストは、操作を完了するために必要な CPU、IO、およびメモリに応じて異なります。 ハードウェア リソースの管理について考える代わりに、各種のデータベース操作を実行しアプリケーション要求を処理するのに必要なリソースに関する単一の測定単位として要求単位 (RU) を考えることができます。
コンテナーごとに設定された要求ユニットの数に基づいて、スループットをプロビジョニングします。 要求単位の消費は、1 秒あたりのレートとして評価されます。 コンテナーのプロビジョニング済み要求ユニット レートを超過したアプリケーションは、レートがそのコンテナーにプロビジョニングされているレベルを下回るまで制限されます。 アプリケーションでより高いスループットが必要になった場合は、追加の要求ユニットをプロビジョニングしてスループットを増やすことができます。
クエリの複雑さは、操作で消費される要求ユニット数に影響します。 述語の数、述語の特性、UDF 数、ソース データ セットのサイズのすべてがクエリ操作のコストに影響します。
操作 (作成、更新、または削除) のオーバーヘッドを測定するには、x-ms-request-charge ヘッダーを調べて、これらの操作で使われる要求ユニット数を測定します。 ResourceResponse<T> または FeedResponse<T> で同等の RequestCharge プロパティを確認することもできます。
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
ResourceResponse<Document> response = asyncClient.createDocument(collectionLink, documentDefinition, null, false).toBlocking.single(); response.getRequestCharge();このヘッダーで返される要求の使用量は、プロビジョニングしたスループットの一部です。 たとえば、2000 RU/秒がプロビジョニングされていて、上記のクエリで 1,000 1 KB のドキュメントが返された場合、操作のコストは 1000 になります。 そのため、後続の要求をレート制限する前に、サーバーは 1 秒以内にこのような要求を 2 つだけ受け付けます。 詳細については、要求ユニットに関する記事および要求ユニット計算ツールのページを参照してください。
レート制限と大きすぎる要求レートに対処する
クライアントがアカウントの予約済みスループットを超えようとしても、サーバーでパフォーマンスの低下が発生することはなく、予約済みのレベルを超えてスループット容量が使用されることもありません。 サーバーはいち早く RequestRateTooLarge (HTTP 状態コード 429) で要求を終了させ、要求を再試行するまでにユーザーが待機しなければならない時間 (ミリ秒) を示す x-ms-retry-after-ms ヘッダーを返します。
HTTP Status 429, Status Line: RequestRateTooLarge x-ms-retry-after-ms :100SDK はすべてこの応答を暗黙的にキャッチし、サーバーが指定した retry-after ヘッダーを優先して要求を再試行します。 アカウントに複数のクライアントが同時アクセスしている状況でなければ、次回の再試行は成功します。
複数のクライアントが累積的に要求レートを超えて一貫して動作している場合、クライアントによって内部的に現在 9 に設定されている既定の再試行回数では十分ではない可能性があります。この場合、クライアントは状態コード 429 の DocumentClientException をアプリケーションにスローします。 既定の再試行回数は、ConnectionPolicy インスタンスで setRetryOptions を使用して変更できます。 既定では、要求が要求レートを超えて動作し続ける場合、状態コード 429 の DocumentClientException は、累積待機時間が 30 秒後に返されます。 これは、現在の再試行回数が最大再試行回数 (既定値の 9 またはユーザー定義の値) より少ない場合でも発生します。
自動再試行動作により、ほとんどのアプリケーションの回復性とユーザービリティが向上しますが、パフォーマンス ベンチマークの実行時 (特に待機時間の測定時) に問題が生じることがあります。 実験でサーバーが負荷制限に達してクライアント SDK が通知なしに再試行すると、クライアントが観測する待機時間が急増します。 パフォーマンスの実験中に待機時間が急増するのを回避するには、各操作で返される使用量を測定し、予約済みの要求レートを下回った状態で要求が行われていることを確認します。 詳細については、 要求ユニットに関する記事を参照してください。
スループットを向上させるためにサイズの小さいドキュメントに合わせて設計する
特定の操作の要求の使用量 (要求処理コスト) は、ドキュメントのサイズに直接関係します。 サイズの大きいドキュメントの操作は、サイズの小さいドキュメントの操作よりもコストがかかります。
次のステップ
スケーリングと高パフォーマンスのためのアプリケーションの設計について詳しくは、「Azure Cosmos DB でのパーティション分割とスケーリング」をご覧ください。