我正在创建基于Cosmos DB和ASP.NET Core 3.0的API。使用Cosmos DB 4.0预览版1 .NET Core SDK。我使用OFFSET和LIMIT子句实现了分页。我看到RU费用显着增加,这是因为您转到的页数越高。页面大小为100的示例:
Page 1: 9.78 RU
Page 10: 37.28 RU
Page 100: 312.22 RU
Page 500: 358.68 RU
查询很简单:
选择*从c偏移[页面*大小] LIMIT [大小]
我做错什么了吗? OFFSET是否需要扫描整个逻辑分区?我要查询的分区中有大约10000个项目的单个分区键。似乎分区中的项目越多,性能越差。 (有关此功能,另请参见uservoice中“ Russ”的注释)。
是否有更好的方法来实现整个分区的有效分页?
编辑1:此外,我注意到在具有10,000个项目的分区中执行OFFSET / LIMIT时,在Cosmos仿真器中执行查询也会使waaayyy速度变慢。
编辑2:这是我用于查询的存储库代码。本质上,它包装了Container.GetItemQueryStreamIterator()方法并在处理IAsyncEnumerable时拉出RU。查询本身就是上面的SQL字符串,那里没有LINQ或其他谜团。
public async Task<RepositoryPageResult<T>> GetPageAsync(int? page, int? pageSize, EntityFilters filters){
// Enforce default page and size if null
int validatedPage = GetValidatedPageNumber(page);
int validatedPageSize = GetValidatedPageSize(pageSize);
IAsyncEnumerable<Response> responseSet = cosmosService.Container.GetItemQueryStreamIterator(
BuildQuery(validatedPage, validatedPageSize, filters),
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(ResolvePartitionKey())
});
var pageResult = new RepositoryPageResult<T>(validatedPage, validatedPageSize);
await foreach (Response response in responseSet)
{
LogResponse(response, COSMOS_REQUEST_TYPE_QUERY_ITEMS); // Read RU charge
if (response.Status == STATUS_OK && response.ContentStream != null)
{
CosmosItemStreamQueryResultSet<T> responseContent = await response.ContentStream.FromJsonStreamAsync<CosmosItemStreamQueryResultSet<T>>();
pageResult.Entities.AddRange(responseContent.Documents);
foreach (var item in responseContent.Documents)
{
cache.Set(item.Id, item); // Add each item to cache
}
}
else
{
// Unexpected status. Abort processing.
return new RepositoryPageResult<T>(false, response.Status, message: "Unexpected response received while processing query response.");
}
}
pageResult.Succeeded = true;
pageResult.StatusCode = STATUS_OK;
return pageResult;
}
编辑3:
在cosmos.azure.com中运行相同的原始SQL,我在查询统计中注意到:
OFFSET 0 LIMIT 100: Output document count = 100, Output document size = 44 KB
OFFSET 9900 LIMIT 100: Output document count = 10000, Output document size = 4.4 MB
确实,检查浏览器中的“网络”选项卡会发现100个单独的HTTP查询,每个查询都检索100个文档!因此,OFFSET似乎当前不在数据库中,而是在客户端上,客户端在丢弃前99个查询中的所有数据之前先检索所有内容。这不是预期的设计吗?该查询是否应该告诉数据库仅在1个响应中返回总共100个项目,而不是全部返回10000个,以便客户端可以丢弃9900个?
答案 0 :(得分:1)
答案 1 :(得分:0)
它在官方文档中以注释https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-offset-limit
的形式在此处进行了说明。具有偏移量限制的查询的RU费用将随着被偏移量的术语数量的增加而增加。对于具有多页结果的查询,我们通常建议使用延续标记。继续令牌是查询以后可以继续进行的地方的“书签”。如果使用OFFSET LIMIT,则没有“书签”。如果要返回查询的下一页,则必须从头开始。