Azure Cosmos DB使用C# .Net连接与查询性能优化问题及ReadThroughputAsync影响咨询
Hey there, let's break down your Cosmos DB performance issues and the curious behavior with ReadThroughputAsync() step by step.
First: Fix the Connection Overhead with Singleton CosmosClient
The biggest issue here is likely how you're creating CosmosClient—right now you're instantiating a new one every time Initialize() runs, which defeats its built-in connection pooling.
CosmosClient is designed to be a singleton across your entire application. It internally manages a pool of connections to Cosmos DB, so reusing the same instance eliminates the 2.5-second connection setup cost you're seeing. In ASP.NET, you should register it as a singleton in your dependency injection container:
// In Program.cs builder.Services.AddSingleton<CosmosClient>(sp => { const string endpointUri = "https://myServer.documents.azure.com:443/"; const string primaryKey = "xxxxxxx=="; return new CosmosClient(endpointUri, primaryKey, new CosmosClientOptions { ApplicationName = "DataImporter" // Optional: Use ConnectionMode.Direct for better performance if your environment allows it // ConnectionMode = ConnectionMode.Direct }); });
Then inject this singleton into your service class instead of creating a new CosmosClient in Initialize():
private readonly Container _container; // Inject the singleton CosmosClient via constructor public YourDataService(CosmosClient cosmosClient) { var database = cosmosClient.GetDatabase("Results"); _container = database.GetContainer("Items"); }
This alone will drastically reduce your connection-related latency, as the connection pool is reused across all requests.
Why ReadThroughputAsync() Changes Timings
Let's unpack that curious timing difference:
- When you call
ReadThroughputAsync(): This method sends an actual HTTP request to Cosmos DB to fetch the container's throughput settings. SinceCosmosClientlazily initializes connections, this request triggers the connection pool setup duringInitialize(), which adds ~2 seconds to your Initialize time. But when you run the query later, the connection is already established, so the query doesn't have to wait for connection setup—hence the slightly faster query time (8.1s vs 8.9s). - When you don't call it:
Initialize()only creates client-side objects (CosmosClient,Database,Container) without sending any requests to Cosmos. The connection setup happens during the first query, so that 2-second connection cost gets added to your query time instead of Initialize.
It's just a matter of where the connection overhead is being counted—no magic, just lazy initialization at work.
Optimizing Query Performance
Your 8-second query for 600 rows is slower than expected. Try these tweaks:
Projection Queries: Instead of
SELECT *, only fetch the fields you need. For example:SELECT c.id, c.name, c.timestamp FROM cThis reduces the amount of data transferred over the network, which can cut query time significantly.
Adjust
MaxItemCount: By default, the query iterator fetches small batches of data. Setting a higherMaxItemCount(like 1000) reduces the number of round trips to Cosmos DB:var queryResultSetIterator = _container.GetItemQueryIterator<SampleData>( queryDefinition, requestOptions: new QueryRequestOptions { MaxItemCount = 1000 } );Check Indexing Strategy: Ensure your container's index policy is optimized. The default policy indexes all paths, but if you have large unneeded fields (like big JSON blobs), excluding them from indexing can speed up scans.
Region Co-Location: Make sure your Azure Web Service is hosted in the same Azure region as your Cosmos DB account. Cross-region network latency can add hundreds of milliseconds per request, which adds up over multiple query pages.
Fix Your Timing Bug: In your original code, you miscalculated query time—you used
tickCount(Initialize's start time) instead oftickCount2(query's start time):var tickCount2 = Environment.TickCount; // ... run query ... tickCount2 = Environment.TickCount - tickCount2; // Correct calculation WriteLine($"Found {results.Count} results in {tickCount2} ms");
Final Recommendations
- Always use a singleton
CosmosClient: This is non-negotiable for connection pooling and performance. - Pre-warm connections (optional): If you want to avoid first-request latency, call a lightweight operation like
ReadThroughputAsync()during app startup (not per request) to initialize the connection pool. - Optimize queries and indexing: Tailor your queries and index policy to only work with the data you need.
内容的提问来源于stack exchange,提问作者dan

