我正在将我的应用程序 azure 功能从 V3 升级到 V4。在此过程中,我还从旧版/不再支持的 Microsoft.Azure.DocumentDB (V 2.18.0) 升级到最新的 Microsoft.Azure.Cosmos(根据建议为 3.32)。问题是,执行此操作现在需要花费近 3时间是发出基本获取请求的时间的两倍,我们看到每个请求都是查询与读取。
下面是我们调用提供的 ReadItemAsync(id,partition,options,token) 的示例。返回的有效负载约为 589 字节。对此的诊断结果是,返回需要 0.400 - 900ms 毫秒!
这不能忍受。
我不知道如何解决这个问题。如果 MS 每次获取需要 500 - 1000 毫秒.. 而我只想运行 26 个项目.. 这将花费近 25 秒的时间。怎么会这样呢?这太糟糕了。当我运行我的方法时,我执行一次获取、一次保存和一次更新插入。在旧库上,完成一次迭代大约需要 300 毫秒,在 3.31.2 上则需要 > 1500 毫秒。
我不知道在哪里或如何解决在 azure 农场花费 460 毫秒的请求。
原始数据调用如下所示:
response = await _database.GetContainer(containerId)
.ReadItemAsync<T>(id, partitionKey, null, cancellationToken);
LastQueryUsage = response.RequestCharge;
return response;
Diagnostics Dump from the above Read request:
{
"Summary": {
"DirectCalls": {
"(200, 0)": 1
},
"GatewayCalls": {
"(200, 0)": 3,
"(304, 0)": 1
}
},
"name": "ReadItemAsync",
"id": "0add6a37-9928-4145-aed1-b29e910e22f3",
"start time": "12:55:11:446",
"duration in milliseconds": 928.666,
//reduced for brevity in light of initial answer.
}
----------马克回复后编辑:----------
我的测试集合仍然表现不佳。很坏。 我启动了一个全新的 Azure V4 Dotnet6 独立项目。
public class CosmosSingleTonConnection
{
private static TestSettings _settings = new TestSettings();
private static readonly List<(string, string)> containers = new()
{
("myDb", "col1"),
("myDb", "col2")
};
private static CosmosClient cosmosClient;
private static Container Raw;
private static Container State;
public Container Container1=> Raw;
public Container Container2=> State;
public CosmosSingleTonConnection(IOptions<TestSettings> settings)
{
_settings = settings.Value;
cosmosClient = InitializeCosmosClient(_settings.Key, _settings.Endpoint);
Raw = cosmosClient.GetDatabase("myDb").GetContainer("col1");
State = cosmosClient.GetDatabase("myDb").GetContainer("col2");
}
private CosmosClient InitializeCosmosClient(string key, string endpoint)
{
return CosmosClient
.CreateAndInitializeAsync(accountEndpoint: endpoint, authKeyOrResourceToken: key, containers: containers, null, CancellationToken.None)
.Result;
}
---Program.cs ---
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
builder
.AddApplicationInsights(opt => { opt.EnableHeartbeat = true; })
.AddApplicationInsightsLogger();
})
.ConfigureServices(DoConfiguration)
.Build();
void DoConfiguration(IServiceCollection services)
{
services.AddOptions<TestSettings>()
.Configure<IConfiguration>((settings, configuration) => { configuration.Bind(settings); });
services.AddSingleton<CosmosSingleTonConnection>();
services.AddScoped<IDoStuffService, DoStuffService>();
}
host.Run();
---DoStuffService---
private readonly CosmosSingleTonConnection _db;
public DoStuffService(CosmosSingleTonConnection db)
{
_db = db;
}
public FeedIterator<ObjectDTO> QueryLast30(string sensor)
{
string top30 = @"Select * from Col1 r Where r.paritionKey= @partitionKey"; //" Order by r.DateTimeCreatedUtc";
QueryRequestOptions ops = new QueryRequestOptions()
{
PartitionKey = new PartitionKey(sensor)
};
var query = new QueryDefinition(top30).WithParameter("@partitionKey", sensor);
using FeedIterator<ObjectDTO> feed = _db.Container1().GetItemQueryIterator<ObjectDTO>(queryDefinition: query, null, null);
return feed;
}
---功能---
public Function1(ILoggerFactory loggerFactory, IDoStuffService service)
{
_logger = loggerFactory.CreateLogger<Function1>();
Service = service;
}
[Function("Function1")]
public async Task<HttpResponseData> RunAsync([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
var response = req.CreateResponse(HttpStatusCode.OK);
List<string> responseTimes = new();
for (int i = 0; i < 10; i++)
{
var feed = Service.QueryLast30("01020001");
while (feed.HasMoreResults)
{
FeedResponse<RawSensorData> fr = await feed.ReadNextAsync();
responseTimes.Add(fr.Diagnostics.GetClientElapsedTime().TotalMilliseconds.ToString());
}
}
response.WriteString(string.Join(" | ", responseTimes));
return response;
}
'----初始加上后续请求---` 这已经是最好的了吗?因为如果每次迭代我必须对 cosmos 执行 4 次原子操作,这就不好了。
459.3067 | 86.5555 | 86.5555 421.989 | 421.989 81.4663 | 426.62 | 426.62 81.7712 | 82.6038 | 78.9875 | 78.9875 81.0167 | 79.0283 201.5111 | 86.7607 | 86.7607 79.1739 | 79.1739 83.5416 | 79.2815 | 79.2815 80.5983 | 79.8568 | 83.7092 | 79.7441 | 79.7441 79.3132 81.8724 | 79.7575 | 79.7575 91.6382 | 80.5015 | 81.7875 | 87.2023 | 79.3385 | 79.3385 78.3251 | 78.3251 78.3159 | 78.3159 79.2731 82.8567 | 82.8567 81.5768 | 81.6155 | 81.535 | 81.5871 | 79.2668 | 79.6522 | 78.9888 | 79.2734 | 79.2734 80.0451 81.1635 | 88.578 | 111.7357 | 84.9948 | 80.207 | 80.207 81.2129 | 79.9344 | 79.9344 80.1654 | 79.4129 | 79.4129 82.7971
最佳答案
这可能是由于首次访问容器对象时的元数据请求或由于首次调用容器时建立了与容器的连接。
最佳实践是使用单例 CosmosClient 并缓存名称或数据库和容器对象,并将对这些对象的引用保留在应用程序的生命周期内。
如果您在每次调用时创建和销毁这些引用,则当 Cosmos Client 从主分区获取元数据以及建立与服务的连接时,您的应用程序将遭受性能高延迟。一旦这些引用和连接就位(在第一次调用服务之后),所有后续的服务调用都会很快。
快速检查我们的 .NET v3 迁移指南和这些性能提示也是个好主意:
关于azure - 与旧库相比,新库的 Cosmos DB 速度非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75179719/