具有OFFSET和LIMIT的Cosmos DB分页性能

时间:2019-11-08 18:08:33

标签: azure-cosmosdb

我正在创建基于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个?

2 个答案:

答案 0 :(得分:1)

基于code,这意味着客户端正在跳过文档,从而增加了RU。

我在浏览器(cosmos.azure.com,使用JS SDK)上测试了相同的场景,并且行为是相同的,随着偏移的移动,RU会增加。

Paging in cosmos.azure.com

Paging in cosmos.azure.com increasing OFFSET

答案 1 :(得分:0)

它在官方文档中以注释https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-offset-limit

的形式在此处进行了说明。

具有偏移量限制的查询的RU费用将随着被偏移量的术语数量的增加而增加。对于具有多页结果的查询,我们通常建议使用延续标记。继续令牌是查询以后可以继续进行的地方的“书签”。如果使用OFFSET LIMIT,则没有“书签”。如果要返回查询的下一页,则必须从头开始。