Azure Cosmos DB .NET SDK のベスト プラクティス

適用対象: NoSQL

この記事では、Azure Cosmos DB .NET SDK を使用する上でのベスト プラクティスについて説明します。 これらのプラクティスに従うことで、待機時間の短縮と高可用性の実現が可能になり、全体的なパフォーマンスを向上させることができます。

以下のビデオをご覧になり、Azure Cosmos DB エンジニアから .NET SDK の使用方法を学んでください。

チェック リスト

オン サブジェクト 詳細/リンク
SDK バージョン 最適なパフォーマンスを実現するために、常に最新バージョンの Azure Cosmos DB SDK を使用します。
シングルトン クライアント パフォーマンスを向上させるには、ご自身のアプリケーションの有効期間中に CosmosClient単一インスタンスを使用します。
リージョン 待機時間を短縮するには、可能な限り、Azure Cosmos DB アカウントと同じ Azure リージョンでアプリケーションを実行してください。 2 から 4 のリージョンを有効にし、複数のリージョンでアカウントをレプリケートして、可用性を最大限に高めます。 運用環境ワークロードの場合は、サービスマネージド フェールオーバーを有効にします。 この構成がない場合、リージョンに接続されていないため手動フェールオーバーは成功せず、書き込みリージョンの停止中は、アカウントで書き込みを利用できなくなる可能性があります。 .NET SDK を使用して複数のリージョンを追加する方法については、こちらを参照してください
可用性とフェールオーバー v3 SDK で ApplicationPreferredRegions または ApplicationRegion を設定し、優先リージョン リストを使用して v2 SDK の PreferredLocations を設定します。 フェールオーバー中に、書き込み操作が現在の書き込みリージョンに送信され、すべての読み取りが優先リージョン リスト内の最初のリージョンに送信されます。 リージョン内フェールオーバー メカニズムの詳細については、可用性に関するトラブルシューティング ガイドを参照してください。
CPU クライアント マシン上のリソースが不足しているため、接続/可用性の問題に遭遇することがあります。 Azure Cosmos DB クライアントを実行しているノードで CPU 使用率を監視し、使用率が高い場合はスケールアップ/スケールアウトします。
Hosting 最適なパフォーマンスを実現するには、可能な限り Windows 64 ビットのホスト処理を使用します。 直接モードの待機時間に敏感な運用環境ワークロードの場合、可能な限り、少なくとも 4 コアかつ 8 GB メモリの VM を使用することを強くお勧めします。
接続モード 最高のパフォーマンスを実現するには、直接モードを使用します。 その方法については、V3 SDK のドキュメントまたは V2 SDK のドキュメントを参照してください。
ネットワーク 仮想マシンを使用してアプリケーションを実行する場合は、VM で高速ネットワークを有効にして、高トラフィックが原因で発生するボトルネックを解消し、待機時間や CPU ジッターを減らします。 CPU の最大使用率が 70% 未満であるハイエンドな仮想マシンの使用も検討してください。
一時的なポートの不足 スパースまたは散発的な接続の場合は、IdleConnectionTimeoutPortReuseModePrivatePortPool に設定します。 IdleConnectionTimeout プロパティは、未使用の接続を閉じるまでの時間を制御するのに役立ちます。 これにより、使用されない接続の数が減ります。 既定では、アイドル状態の接続は無期限に開いた状態になります。 10 分以上の値を指定する必要があります。 推奨値は 20 分から 24 時間です。 PortReuseMode プロパティにより、SDK ではさまざまな Azure Cosmos DB の宛先エンドポイントに対して一時的なポートの小さなプールを使用できます。
Async/Await の使用 Task.ResultTask.WaitTask.GetAwaiter().GetResult() のブロック呼び出しを避けます。 コール スタック全体を非同期にして、async/await の組み合わせを有効活用する。 多くの同期的なブロッキング呼び出しを行うと、スレッド プールの枯渇や応答時間の増加が起こります。
エンドツーエンドのタイムアウト エンドツーエンドのタイムアウトを取得するには、RequestTimeoutCancellationToken の両方のパラメーターを使用する必要があります。 詳細については、タイムアウトのトラブルシューティング ガイドを参照してください
再試行ロジック 再試行の対象となるエラーと SDK によって再試行されるエラーの詳細については、設計ガイドを参照してください。 複数のリージョンで構成されたアカウントの場合、SDK によって他のリージョンで自動的に再試行されるシナリオがいくつかあります。 .NET 固有の実装の詳細については、SDK ソース リポジトリを参照してください。
データベース/コレクション名のキャッシュ 構成からデータベースとコンテナーの名前を取得するか、開始時にそれらをキャッシュします。 ReadDatabaseAsyncReadDocumentCollectionAsync、また CreateDatabaseQueryCreateDocumentCollectionQuery ような呼び出しでは、サービスに対するメタデータ呼び出しが行われ、システムで予約されている RU 制限から消費されます。 また、CreateIfNotExist は、データベースの設定に 1 回だけ使用する必要があります。 一般に、これらの操作は、頻繁に実行しないでください。
一括サポート 待機時間を最適化する必要がないシナリオでは、大量のデータをダンプするために、一括サポートを有効にすることをお勧めします。
並列クエリ Azure Cosmos DB SDK では、クエリの待機時間とスループットを向上させるクエリの並列実行がサポートされています。 QueryRequestsOptions 内の MaxConcurrency プロパティ を、使用しているパーティションの数に設定することをお勧めします。 パーティションの数を認識していない場合は、int.MaxValue を使用して開始すると、最適な待機時間が得られます。 次に、高 CPU 使用率の問題を回避するために、環境のリソース制限に適合するまで数を減らします。 また、MaxBufferedItemCount を、返される結果の予想される数に設定して、プリフェッチされる結果の数を制限します。
パフォーマンス テストのバックオフ アプリケーションでテストを実行する場合は、RetryAfter の間隔でバックオフを実装する必要があります。 バックオフにより、再試行までの待ち時間を最小限に抑えることができます。
インデックス作成 Azure Cosmos DB のインデックス作成ポリシーでは、インデックス作成パス (IndexingPolicy.IncludedPaths および IndexingPolicy.ExcludedPaths) を使用して、インデックス作成に含めたり除外したりするドキュメント パスも指定できます。 書き込みを高速化するには、インデックス作成から未使用のパスを除外ししてください。 SDK を使用してインデックスを作成する方法の詳細については、「パフォーマンスに関する ヒント .NET SDK v3」 を参照してください。
ドキュメント サイズ 特定の操作の要求の料金は、ドキュメントのサイズに直接関係します。 サイズの大きいドキュメントの操作は、サイズの小さいドキュメントの操作よりもコストがかかるので、ドキュメントのサイズを小さくすることをお勧めします。
スレッドまたはタスクの数を増やす Azure Cosmos DB の呼び出しはネットワーク経由で行われるため、クライアント アプリケーションで要求間の待ち時間を最短にするために、要求のコンカレンシーの次数を変えることが必要な場合があります。 たとえば、.NET のタスク並列ライブラリを使用する場合、Azure Cosmos DB に対する読み取りまたは書き込みのタスクを 100 件単位で作成してください。
クエリ メトリックの有効化 バックエンド クエリの実行のログ記録を追加する場合は、.NET SDK を使用して SQL クエリ メトリックを有効にできます。 SQL クエリ メトリックを収集する方法の詳細については、「クエリ メトリックとパフォーマンス」 を参照してください。
SDK のログ 例外時や、要求が予想される待機時間を超える場合など、未処理のシナリオの SDK 診断をログします。
DefaultTraceListener DefaultTraceListener は、運用環境でのパフォーマンス上の問題を引き起こし、高い CPU 使用率や I/O のボトルネックが発生します。 最新の SDK バージョンを使用していることを確認するか、アプリケーションから DefaultTraceListener を削除してください
識別子で特殊文字を使用しないようにする 一部の文字は制限されており、識別子 '/'、'\'、'?'、'#' では使用できません。 一般的なレコメンデーションは、予期しない動作を回避するために、データベース名、コレクション名、項目 ID、パーティション キーなどの識別子に特殊文字を使用しないことをお勧めします。

診断をキャプチャする

CosmosException を含む SDK のすべての応答には、Diagnostics プロパティがあります。 このプロパティでは、再試行または一時的な失敗があるかどうかなど、1 つの要求に関連するすべての情報が記録されます。

診断は文字列として返されます。 文字列は、さまざまなシナリオのトラブルシューティングに対応するために改良されるので、バージョンによって異なります。 SDK の各バージョンでは、文字列の書式設定に重大な変更が加えられる予定です。 破壊的変更を避けるために、文字列を解析しないでください。 次のコード サンプルは、.NET SDK を使用して診断ログを読み取る方法を示しています。

try
{
    ItemResponse<Book> response = await this.Container.CreateItemAsync<Book>(item: testItem);
    if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan)
    {
        // Log the response.Diagnostics.ToString() and add any additional info necessary to correlate to other logs 
    }
}
catch (CosmosException cosmosException)
{
    // Log the full exception including the stack trace with: cosmosException.ToString()
    
    // The Diagnostics can be logged separately if required with: cosmosException.Diagnostics.ToString()
}

// When using Stream APIs
ResponseMessage response = await this.Container.CreateItemStreamAsync(partitionKey, stream);
if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan || !response.IsSuccessStatusCode)
{
    // Log the diagnostics and add any additional info necessary to correlate to other logs with: response.Diagnostics.ToString()
}

HTTP 接続のベスト プラクティス

.NET SDK では、構成されている接続モードに関係なく、HttpClient を使用して HTTP 要求を実行します。 ダイレクト モードでは HTTP がメタデータ操作に使用され、ゲートウェイ モードではデータ プレーン操作とメタデータ操作の両方に使用されます。 HttpClient の基礎の 1 つは、プールされた接続の有効期間をカスタマイズすることで、HttpClient がアカウントの DNS 変更に対応できるようにすることです。 プールされた接続が開いたままになっている限り、DNS の変更には対応しません。 この設定により、プールされた接続が定期的に閉じられ、アプリケーションが DNS の変更に確実に対応できるようになります。 この値を、接続モードとワークロードに応じてカスタマイズし、頻繁に新しい接続を作成する場合のパフォーマンスへの影響と、DNS の変更に対応する必要性 (可用性) のバランスを取ることをお勧めします。 5 分の値は、適切な開始点になります。特にゲートウェイ モードの場合にパフォーマンスに影響がある場合は、増加することができます。

次のように、CosmosClientOptions.HttpClientFactory を使用してカスタム HttpClient を挿入できます。

// Use a Singleton instance of the SocketsHttpHandler, which you can share across any HttpClient in your application
SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler();
// Customize this value based on desired DNS refresh timer
socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);

CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
    // Pass your customized SocketHttpHandler to be used by the CosmosClient
    // Make sure `disposeHandler` is `false`
    HttpClientFactory = () => new HttpClient(socketsHttpHandler, disposeHandler: false)
};

// Use a Singleton instance of the CosmosClient
return new CosmosClient("<connection-string>", cosmosClientOptions);

.NET 依存関係の挿入を使用する場合は、シングルトン プロセスを簡略化できます。

SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler();
// Customize this value based on desired DNS refresh timer
socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);
// Registering the Singleton SocketsHttpHandler lets you reuse it across any HttpClient in your application
services.AddSingleton<SocketsHttpHandler>(socketsHttpHandler);

// Use a Singleton instance of the CosmosClient
services.AddSingleton<CosmosClient>(serviceProvider =>
{
    SocketsHttpHandler socketsHttpHandler = serviceProvider.GetRequiredService<SocketsHttpHandler>();
    CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
    {
        HttpClientFactory = () => new HttpClient(socketsHttpHandler, disposeHandler: false)
    };

    return new CosmosClient("<connection-string>", cosmosClientOptions);
});

ゲートウェイ モードを使用する場合のベスト プラクティス

ゲートウェイ モードを使用する場合は、ホストごとに System.Net MaxConnections を増やします。 ゲートウェイ モードを使用すると、Azure Cosmos DB の要求は HTTPS/REST を介して行われます。 それらは、ホスト名または IP アドレスごとの既定の接続数の上限に従います。 場合によっては、Azure Cosmos DB に対する複数の同時接続をクライアント ライブラリで使用できるよう、MaxConnections を高い値 (100 ~ 1,000) に増やす必要があります。 .NET SDK 1.8.0 以降では、ServicePointManager.DefaultConnectionLimit の既定値は 50 です。 値を変更するには、CosmosClientOptions.GatewayModeMaxConnectionLimit を高い値に設定します。

書き込み負荷の高いワークロードのベスト プラクティス

高負荷の作成ペイロードがあるワークロードでは、EnableContentResponseOnWrite 要求オプションを false に設定します。 サービスは、作成または更新されたリソースを SDK に返さなくなります。 通常、アプリケーションには作成済みのオブジェクトがあるため、サービスから返される必要はありません。 ヘッダー値には、まだ要求の料金と同様にアクセスできます。 応答コンテンツを無効にすると、SDK がメモリを割り当てたり応答の本文をシリアル化したりする必要がなくなるため、パフォーマンスを向上させることができます。 また、ネットワーク帯域幅の使用量も削減され、パフォーマンスがさらに向上します。

重要

EnableContentResponseOnWritefalse に設定すると、トリガー操作からの応答も無効になります。

マルチテナント アプリケーションのベスト プラクティス

同じ Azure Cosmos DB アカウント内で、異なるデータベース、コンテナー、またはパーティション キーによって各テナントが表される複数のテナントに使用量を分散するアプリケーションは、1 つのクライアント インスタンスを使用する必要があります。 1 つのクライアント インスタンスは、アカウント内のすべてのデータベース、コンテナー、パーティション キーと対話できます。シングルトン パターンを使用することをお勧めします。

ただし、各テナントが異なる Azure Cosmos DB アカウントによって表されるときは、アカウントごとに別個のクライアント インスタンスを作成する必要があります。 それでもシングルトン パターンは各クライアントに適用されますが (アプリケーションの有効期間中アカウントごとに 1 つのクライアント)、テナントのボリュームが多い場合は、クライアントの数を管理するのが困難になる可能性があります。 接続がコンピューティング環境の限界を超えて増加して、接続の問題を引き起こす可能性があります。

このような場合は、次を行うことをお勧めします。

  • コンピューティング環境 (CPU と接続リソース) の限界を把握します。 可能な限り、少なくとも 4 コアと 8 GB のメモリを備えた VM を使用することをお勧めします。
  • コンピューティング環境の限界に基づいて、1 つのコンピューティング インスタンスで処理できるクライアント インスタンスの数 (つまりテナントの数) を決定します。 選択した接続モードに応じて、クライアントごとに開かれる接続の数を見積もることができます。
  • インスタンス間のテナントの分散を評価します。 各コンピューティング インスタンスが特定の限られた量のテナントを正常に処理できる場合、テナントの負荷分散と異なるコンピューティング インスタンスへのルーティングにより、テナント数の増加に応じてスケーリングが可能になります。
  • ワークロードが少ない場合は、クライアント インスタンスを保持し、時間枠内にアクセスされていないテナントのクライアントを破棄する構造として、最も使用頻度の低いキャッシュを使用することを検討してください。 .NET での 1 つの選択肢は MemoryCacheEntryOptions です。これは RegisterPostEvictionCallback を使用して非アクティブなクライアントを破棄し、SetSlidingExpiration を使用して非アクティブな接続を保持する最大時間を定義できます。
  • ゲートウェイ モードを使用して評価し、ネットワーク接続の数を減らします。
  • ダイレクト モードを使用する場合は、使用されていない接続を閉じ、接続の量を制御するために、ダイレクト モード構成CosmosClientOptions.IdleTcpConnectionTimeoutCosmosClientOptions.PortReuseMode を調整することを検討してください。

次のステップ

少数のクライアント コンピューターでの高パフォーマンス シナリオで Azure Cosmos DB の評価に使用されるサンプル アプリケーションについては、「Azure Cosmos DB のパフォーマンスとスケールのテスト」を参照してください。

スケーリングと高パフォーマンスのためのアプリケーションの設計について詳しくは、「Azure Cosmos DB でのパーティション分割とスケーリング」をご覧ください。

Azure Cosmos DB への移行のための容量計画を実行しようとしていますか? 容量計画のために、既存のデータベース クラスターに関する情報を使用できます。