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 來新增多個區域,請造訪這裡
可用性與容錯移轉 在第 3 版 SDK 中設定 ApplicationPreferredRegionsApplicationRegion,並使用慣用的區域清單來設定第 2 版 SDK 中的 PreferredLocations。 在容錯移轉期間,寫入作業會傳送至目前的寫入區域,而且所有讀取都會傳送至您慣用區域清單中的第一個區域。 如需有關區域容錯移轉機制的詳細資訊,請參閱可用性疑難排解指南
CPU 由於用戶端電腦上的資源不足,您可能會遇到連線能力/可用性問題。 監視執行 Azure Cosmos DB 用戶端之節點的 CPU 使用率,並在使用量很高時擴大/縮小。
裝載 盡可能使用 Windows 64 位元主機處理以獲得最佳效能。 針對直接模式延遲敏感性生產工作負載,高度建議盡可能使用至少 4 核心和 8 GB 的記憶體 VM。
連線模式 使用直接模式以獲得最佳效能。 如需如何進行此作業的指示,請參閱第 3 版 SDK 文件第 2 版 SDK 文件
網路 如果使用虛擬機器來執行應用程式,請在 VM 上啟用加速網路,以協助排解因高流量而產生的瓶頸,並減少延遲或 CPU 抖動。 您也可能想要考慮使用較高的終端虛擬機器,其中最大 CPU 使用率低於70%。
暫時連接埠耗盡 針對稀疏或偶爾的連接,我們將 IdleConnectionTimeoutPortReuseMode 設定為 PrivatePortPoolIdleConnectionTimeout 屬性可協助控制未使用連線的關閉時間。 這可減少未使用連線數目。 依預設,閒置連接會無限期地保持開啟狀態。 設定值必須大於或等於 10 分鐘。 我們建議使用介於 20 分鐘到 24 小時的值。 PortReuseMode 屬性可讓 SDK 針對各種 Azure Cosmos DB 目的地端點,使用較小的暫時連接埠集區。
使用非同步/等候 避免封鎖呼叫:Task.ResultTask.WaitTask.GetAwaiter().GetResult()。 整個呼叫堆疊屬於非同步,可受益於非同步/等候模式。 許多同步封鎖呼叫會導致執行緒集區耗盡和回應時間降級。
端對端逾時 若要取得端對端逾時,您需要同時使用 RequestTimeoutCancellationToken 參數。 如需更多詳細資料,請造訪逾時疑難排解指南
重試邏輯 如需要重試的錯誤以及 SDK 所重試錯誤的詳細資訊,請參閱設計指南。 針對已設定多個區域的帳戶,在某些情況下,SDK 將會在其他區域上自動重試。 如需 .NET 特定實作詳細資料,請造訪 SDK 來源存放庫
快取資料庫/集合名稱 從設定擷取資料庫和容器名稱,或在啟動時加以快取。 如 ReadDatabaseAsyncReadDocumentCollectionAsyncCreateDatabaseQueryCreateDocumentCollectionQuery 之類的呼叫會導致服務的中繼資料呼叫,這會從系統保留的 RU 限制取用。 設定資料庫時,應該也只能使用一次 CreateIfNotExist。 整體來說,這些作業應該不常執行。
大量支援 在您可能不需要針對延遲最佳化的情節下,我們建議您啟用大量支援來傾印大量資料。
平行查詢 Azure Cosmos DB SDK 支援平行執行查詢,以提升查詢的延遲和輸送量。 建議您將 QueryRequestsOptions 中的 MaxConcurrency 屬性設定為您擁有的分割區數目。 如果您不知道分割區數目,請從使用 int.MaxValue 開始,讓您獲得最佳的延遲。 然後減少數目,直到符合環境的資源限制,以避免發生高 CPU 問題。 此外,請將 MaxBufferedItemCount 設定為預期傳回的結果數目,以限制預先擷取的結果數目。
效能測試輪詢 在應用程式上執行測試時,您應該依 RetryAfter 間隔實作輪詢。 採用降速有助於確保您在重試之間花費最少的等待時間。
編製索引 Azure Cosmos DB 的編製索引原則也可讓您運用索引路徑 (IndexingPolicy.IncludedPaths and IndexingPolicy.ExcludedPaths),指定要在編製索引中包含或排除的文件路徑。 確保將未使用的路徑排除於索引編製外,以加快寫入速度。 如需如何使用 SDK 建立索引的詳細資訊,請參閱效能秘訣 .NET SDK v3
文件大小 指定作業的要求費用會直接與文件大小相互關聯。 建議您減少文件的大小,因為對大型文件的作業成本較對小文件的作業成本更高。
增加執行緒/工作數目 由於對 Azure Cosmos DB 的呼叫要透過網路進行,因此您可能需要改變要求的並行處理原則程度,以便讓用戶端應用程式在不同要求之間的等待時間降到最低。 例如,如果您使用的是 .NET 工作平行程式庫,請依照從 Azure Cosmos DB 讀取或寫入的數百個工作順序來建立。
啟用查詢計量 如需後端查詢執行的其他記錄,您可以使用 .NET SDK 啟用 SQL 查詢計量。 如需如何收集 SQL 查詢計量的詳細資訊,請參閱查詢計量和效能
SDK 記錄 針對未處理的案例記錄 SDK 診斷,例如例外狀況或是要求超過預期延遲時。
DefaultTraceListener DefaultTraceListener 會在生產環境中造成效能問題,導致高 CPU 和 I/O 瓶頸。 請確定您使用的是最新的 SDK 版本,或從您的應用程式中移除 DefaultTraceListener
避免在識別碼中使用任何特殊字元 部分字元受到限制,而且無法用於部分屬性:'/'、'\'、'?'、'#'。 一般建議不要在識別碼中使用任何特殊字元,例如資料庫名稱、集合名稱、項目 ID 或分割區索引鍵,以避免發生任何非預期的行為。

擷取診斷

SDK 中的所有回應 (包括 CosmosException) 都具有 Diagnostics 屬性。 此屬性記錄與單一要求相關的所有資訊,包括是否有重試或任何暫時性失敗。

診斷以字串形式傳回。 每個版本會改進以針對不同情節進行疑難排解,所以此字串也隨之變更。 在每一版的 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 基礎是「自訂集區式連線存留期」,以確定 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 相依性插入,則可以簡化 Singleton 程序:

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。 當您使用網路閘道模式,系統會透過 HTTPS/REST 發出 Azure Cosmos DB 要求。 這些要求受限於每個主機名稱或 IP 位址的預設連線限制。 您可能需要將 MaxConnections 設為較高的值 (100 至 1000),這樣一來用戶端程式庫就可以使用多個同時連至 Azure Cosmos DB 的連線。 在 .NET SDK 1.8.0 和更新版本中,ServicePointManager.DefaultConnectionLimit 的預設值為 50。 若要變更此值,您可以將 CosmosClientOptions.GatewayModeMaxConnectionLimit 設定為較高的值。

大量寫入工作負載的最佳做法

對於具有大量建立承載的工作負載,請將 EnableContentResponseOnWrite 要求選項設定為 false。 服務就不會再將已建立或已更新的資源傳回至 SDK。 一般來說,因為應用程式具有正在建立的物件,所以不需要服務來傳回物件。 標頭值仍可存取,例如要求費用。 停用內容回應有助於改善效能,因為 SDK 不再需要配置記憶體或序列化回應主體。 這也可減少網路頻寬使用量,進一步提升效能。

重要

EnableContentResponseOnWrite 設定為 false 也會停用觸發程序作業的回應。

多租用戶應用程式的最佳做法

如果應用程式將使用量散發到多個租用戶,而且每個租用戶在相同的 Azure Cosmos DB 帳戶內都是以不同的資料庫、容器或分割區索引鍵表示,則應用程式應該使用單一用戶端執行個體。 單一用戶端執行個體可以與帳戶內的所有資料庫、容器和分割區索引鍵互動,而且最佳做法是使用 Singleton 模式

不過,每個租用戶都是以不同的 Azure Cosmos DB 帳戶表示時,每個帳戶都必須建立個別的用戶端執行個體。 Singleton 模式仍然適用於每個用戶端 (在應用程式存留期,一個帳戶有一個用戶端),但如果租用戶數量很高,則可能難以管理用戶端數目。 連線可能會超出計算環境的限制,並導致連線問題

在這些情況下,建議您:

  • 了解計算環境的限制 (CPU 和連線資源)。 建議盡可能使用至少 4 核心和 8 GB 記憶體的 VM。
  • 根據計算環境的限制,判斷單一計算執行個體可處理的用戶端執行個體數目 (因此,也會判斷租用戶數目)。 您可以根據所選擇的連線模式來預估連線數目,而這些連線是針對每個用戶端所開啟。
  • 評估跨執行個體的租用戶散發。 如果每個計算執行個體都可以成功處理特定有限的租用戶數量,則允許在租用戶數目成長時調整不同計算執行個體的租用戶負載平衡和路由。
  • 針對疏鬆工作負載,請考慮使用「最不常用的快取」作為結構來保存用戶端執行個體,並處置尚未在時間範圍內存取的租用戶用戶端。 .NET 中的其中一個選項是 MemoryCacheEntryOptions,其中 RegisterPostEvictionCallback 可以用來處置非使用中用戶端,而且 SetSlidingExpiration 可以用來定義保存非使用中連線的最長時間。
  • 評估使用閘道模式來減少網路連線數目。
  • 使用直接模式時,請考慮調整直接模式設定上的 CosmosClientOptions.IdleTcpConnectionTimeoutCosmosClientOptions.PortReuseMode 來關閉非使用中連線,並控制連線數量

下一步

如需用來評估 Azure Cosmos DB 在數個用戶端電腦上達到高效能情節的範例應用程式,請參閱 Azure Cosmos DB 的效能和規模測試

若要深入了解如何針對規模和高效能設計您的應用程式,請參閱 Azure Cosmos DB 的資料分割與調整規模

正在嘗試為遷移至 Azure Cosmos DB 進行容量規劃嗎? 您可以使用現有資料庫叢集的相關資訊進行容量規劃。