Я создаю API на основе Cosmos DB и ASP.NET Core 3.0. Использование предварительного просмотра 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 СМЕЩЕНИЕ [размер страницы] ПРЕДЕЛ [размер]
утраЯ делаю что-то не так, или это ожидается? Требуется ли для OFFSET сканирование всего логического раздела? Я запрашиваю один ключ раздела с примерно 10000 элементами в разделе. Кажется, чем больше элементов в разделе, тем хуже производительность. (См. Также комментарий "Russ" в uservoice для этой функции).
Есть ли лучший способ реализовать эффективный пейджинг через весь раздел?
Редактировать 1: Я также замечаю, что выполнение запросов в эмуляторе космоса также замедляет процесс ожидания при выполнении OFFSET / LIMIT враздел с 10 000 элементов.
Редактировать 2: Вот мой код хранилища для запроса. По сути, это обертка метода Container.GetItemQueryStreamIterator () и извлечение RU при обработке IAsyncEnumerable. Сам запрос представляет собой приведенную выше строку 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:
Запуск того же необработанного SQL из cosmos.azure.com, я заметил в статистике запросов:
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 запросов. Это не может быть предполагаемый дизайн? Разве запрос не должен указывать базе данных возвращать всего 100 элементов в одном ответе, а не все 10000, чтобы клиент мог выбросить 9900?