【发布时间】:2020-03-05 09:27:25
【问题描述】:
我正在创建一个基于 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
查询很简单:
SELECT * from c OFFSET [page*size] LIMIT [size]
我做错了什么,还是这是意料之中的? OFFSET 是否需要扫描整个逻辑分区?我正在查询分区中包含大约 10000 个项目的单个分区键。似乎分区中的项目越多,性能就越差。 (有关此功能,另请参阅 uservoice 中“Russ”的评论)。
有没有更好的方法来实现对整个分区的高效分页?
编辑 1:另外,我注意到在 Cosmos 模拟器中执行查询时,在具有 10,000 个项目的分区中执行 OFFSET/LIMIT 时也会减慢速度。
编辑 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?
【问题讨论】:
-
您能说明您是如何进行查询并获取这些 RU 值的吗?
-
@MatiasQuaranta 当然,我已经用存储库代码更新了问题。
-
@MatiasQuaranta 请参阅“编辑 3”,似乎客户端正在跳过,而不是数据库。不是期望数据库返回限制金额,而不是全部吗?
标签: azure-cosmosdb