Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL
Azure Cosmos DB — быстрая и гибкая распределенная база данных, которая легко масштабируется с гарантированными уровнями задержки и пропускной способности. Для масштабирования базы данных с помощью Azure Cosmos DB не нужно вносить в архитектуру существенные изменения или писать сложный код. Для увеличения или уменьшения масштаба достаточно выполнить один вызов API. Дополнительные сведения см. в разделах о подготовке пропускной способности контейнера и о подготовке пропускной способности базы данных.
Уменьшите количество вызовов плана запросов
Чтобы выполнить запрос, необходимо создать план запроса. Сетевые запросы к шлюзу Azure Cosmos DB увеличивают задержку выполнения операции запроса. Существует два способа, позволяющих удалить этот запрос и уменьшить задержку операции запроса:
Оптимизация запросов к единому разделу с помощью оптимистичного прямого исполнения
Azure Cosmos DB NoSQL имеет оптимизацию под названием Оптимистическое прямое выполнение (ODE), что может повысить эффективность определенных запросов NoSQL. В частности, запросы, которые не требуют распределения, включают те, которые могут выполняться в одной физической секции или имеют ответы, которые не требуют разбиения на страницы. Запросы, которые не требуют распространения, могут уверенно пропускать некоторые процессы, такие как создание плана запросов на стороне клиента и перезапись запросов, что снижает задержку запросов и затраты единиц запросов (ЕЗ). Если в запросе или запросе указан ключ секции (или только одна физическая секция), а результаты запроса не требуют разбиения на страницы, ODE может улучшить запросы.
Примечание.
ODE, который обеспечивает улучшенную производительность запросов, которые не требуют распределения, не следует путать с прямым режимом, который является путем подключения приложения к внутренним репликам.
Теперь ODE доступен в пакете SDK для .NET версии 3.38.0 и более поздних версий. При выполнении запроса и указании ключа раздела в самом запросе или при наличии у базы данных только одного физического раздела, выполнение запроса может использовать преимущества ODE. Чтобы включить ODE, задайте EnableOptimisticDirectExecution значение true в QueryRequestOptions.
Запросы с отдельным разделом данных, которые включают GROUP BY, ORDER BY, DISTINCT и функции агрегирования (например, сумма, среднее, минимум и максимум), могут значительно выиграть от использования ODE. Однако в сценариях, когда запрос предназначен для нескольких разделов или по-прежнему требует разбиения на страницы, задержка ответа на запрос и затраты на RU могут быть выше, чем без использования ODE. Поэтому при использовании ODE следует:
- Укажите ключ раздела непосредственно в вызове или запросе.
- Убедитесь, что размер данных не вырос и вызвал разделение секции.
- Убедитесь, что результаты запроса не требуют разбиения на страницы, чтобы получить полное преимущество ODE.
Ниже приведены несколько примеров простых запросов с одиночным разделом, которые могут извлечь выгоду из использования ODE:
- SELECT * FROM r
- SELECT * FROM r WHERE r.pk == "value"
- SELECT * FROM r WHERE r.id > 5
- SELECT r.id FROM r JOIN id IN r.id
- SELECT TOP 5 r.id FROM r ORDER BY r.id
- SELECT * FROM r WHERE r.id > 5 OFFSET 5 LIMIT 3
В случаях, когда запросы к одному разделу могут по-прежнему требовать распределения, если со временем увеличивается количество элементов данных, и база данных Azure Cosmos DB разделяет этот раздел. Примеры запросов, в которых это может произойти, включают:
- SELECT Count(r.id) AS count_a FROM r
- SELECT DISTINCT r.id FROM r
- SELECT Max(r.a) as min_a FROM r
- SELECT Avg(r.a) as min_a FROM r
- SELECT Sum(r.a) as sum_a FROM r WHERE r.a > 0
Некоторые сложные запросы могут всегда требовать распределения, даже если они направлены на один раздел. Примеры таких запросов:
- SELECT Sum(id) as sum_id FROM r JOIN id IN r.id
- SELECT DISTINCT r.id FROM r GROUP BY r.id
- SELECT DISTINCT r.id, Sum(r.id) as sum_a FROM r GROUP BY r.id
- SELECT Count(1) FROM (SELECT DISTINCT r.id FROM root r)
- SELECT Avg(1) AS avg FROM root r
Важно отметить, что ODE может не всегда извлекать план запроса, и, в результате, не может запретить или отключить неподдерживаемые запросы. Например, после разделения разделов такие запросы больше не важны для ODE и, следовательно, не будут выполняться, так как оценка плана запроса на стороне клиента блокирует их. Чтобы обеспечить непрерывность совместимости и обслуживания, важно убедиться, что с ODE используются только запросы, которые полностью поддерживаются в сценариях без ODE (то есть выполняются и создают правильный результат в общем случае с несколькими секциями).
Примечание.
Использование ODE может привести к созданию нового типа маркера продолжения. Такой токен не распознается старыми версиями SDK изначально, и это может привести к исключению неправильного маркера продолжения. Если у вас есть сценарий, в котором маркеры, созданные из новых пакетов SDK, используются старым пакетом SDK, рекомендуется выполнить двухфакторный подход к обновлению:
- Обновите новый пакет SDK и отключите ODE как часть одного развертывания. Дождитесь обновления всех узлов.
- Чтобы отключить ODE, установите
EnableOptimisticDirectExecutionв значение false в QueryRequestOptions. - Включите ODE в рамках второго развертывания для всех узлов.
Используйте создание плана локального запроса
Пакет SDK SQL включает собственный ServiceInterop.dll для анализа и оптимизации запросов локально. ServiceInterop.dll поддерживается только на платформе Windows x64 . В следующих типах приложений по умолчанию используется 32-разрядная обработка узлов. Чтобы изменить обработку узлов на 64-разрядную, выполните следующие действия в зависимости от типа приложения.
Для исполняемых приложений можно изменить обработку узла, задав для параметра Целевая платформа значение x64 в окне Свойства проекта на вкладке Сборка.
Для тестовых проектов на основе VSTest можно изменить обработку узла, выбрав Тест>Параметры тестирования>Архитектура процессора по умолчанию — X64 в пункте меню Тест в Visual Studio.
Для локально развернутых веб-приложений ASP .NET можно изменить обработку узла, установив флажок Использовать 64-разрядную версию IIS Express для веб-сайтов и проектов в меню Сервис>Параметры>Проекты и решения>Веб-проекты.
Для веб-приложений ASP .NET, развернутых в Azure, можно изменить обработку узлов, выбрав 64-разрядную платформу в окне Параметры приложения на портале Azure.
Примечание.
По умолчанию для новых проектов Visual Studio задано значение Любой ЦП. Рекомендуется задать для проекта значение x64, чтобы он не переключился на x86. Проект, для которого задано значение Любой ЦП, может легко переключиться на x86, если добавляется зависимость только для x86.
ServiceInterop.dll должен находиться в папке, из которой выполняется библиотека DLL ПАКЕТА SDK. Это может вызывать проблемы только в том случае, если вы вручную копируете библиотеки DLL или используете пользовательские сборки или системы развертывания.
Используйте односекционные запросы
Для запросов, предназначенных для ключа раздела при установке свойства PartitionKey в QueryRequestOptions; и которые не содержат агрегирования (включая Distinct, DCount или Group By). В этом примере поле /state ключа секции фильтруется по значению Washington.
using (FeedIterator<MyItem> feedIterator = container.GetItemQueryIterator<MyItem>(
"SELECT * FROM c WHERE c.city = 'Seattle' AND c.state = 'Washington'"
{
// ...
}
При необходимости ключ секции можно указать как часть объекта параметров запроса.
using (FeedIterator<MyItem> feedIterator = container.GetItemQueryIterator<MyItem>(
"SELECT * FROM c WHERE c.city = 'Seattle'",
requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Washington")}))
{
// ...
}
Внимание
На клиентах под управлением операционной системы, отличной от Windows, например Linux и macOS, ключ раздела всегда должен быть указан в объекте параметров запроса.
Примечание.
Для запросов между разделами пакет SDK должен посетить все существующие разделы для проверки результатов. Чем больше физических секций имеет контейнер, тем вероятнее его производительность может снизиться.
Не создавайте итератор повторно без необходимости
Если все результаты запроса используются текущим компонентом, вам не нужно повторно создавать итератор с продолжением для каждой страницы. Всегда обрабатывайте запрос полностью, если разбиение на страницы не контролируется другим компонентом вызова:
using (FeedIterator<MyItem> feedIterator = container.GetItemQueryIterator<MyItem>(
"SELECT * FROM c WHERE c.city = 'Seattle'",
requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Washington")}))
{
while (feedIterator.HasMoreResults)
{
foreach(MyItem document in await feedIterator.ReadNextAsync())
{
// Iterate through documents
}
}
}
Настройка степени параллелизма
Настройте свойство MaxConcurrency в QueryRequestOptions для запросов, чтобы определить оптимальные конфигурации для приложения, особенно если выполняются запросы между секциями (без фильтрации по значению ключа секции). Параметр MaxConcurrency определяет максимальное число параллельных задач, т. е. максимальное количество секций, обрабатываемых одновременно. Установка значения -1 позволяет пакету SDK решить оптимальную параллельность.
using (FeedIterator<MyItem> feedIterator = container.GetItemQueryIterator<MyItem>(
"SELECT * FROM c WHERE c.city = 'Seattle'",
requestOptions: new QueryRequestOptions() {
PartitionKey = new PartitionKey("Washington"),
MaxConcurrency = -1 }))
{
// ...
}
Предположим, что:
- D = максимальное число параллельных задач по умолчанию (= общее количество процессоров на клиентском компьютере)
- P — определенное пользователем максимальное число параллельных задач;
- N — количество секций, которые необходимо посетить для получения ответов на запросы.
Ниже приведены последствия поведения параллельных запросов для разных значений P.
- (P == 0) => последовательный режим
- (P == 1) => не более одной задачи;
- (P > 1) => минимальное значение (P, N) параллельных задач;
- (P < 1) => минимум (N, D) параллельных задач.
Настройка размера страницы
При выполнении SQL-запроса если результирующий набор имеет слишком большой размер, результаты возвращаются в сегментированном виде.
Примечание.
Свойство MaxItemCount не должно использоваться только для разбиения на страницы. Его основное применение — повышение производительности запросов за счет уменьшения максимального числа элементов, возвращаемых на одной странице.
Размер страницы также можно изменить с помощью доступных пакетов SDK для Azure Cosmos DB. Свойство MaxItemCount в QueryRequestOptions позволяет задать максимальное число элементов, возвращаемых операцией перечисления. Если MaxItemCount задано значение -1, пакет SDK автоматически находит оптимальное значение в зависимости от размера документа. Например:
using (FeedIterator<MyItem> feedIterator = container.GetItemQueryIterator<MyItem>(
"SELECT * FROM c WHERE c.city = 'Seattle'",
requestOptions: new QueryRequestOptions() {
PartitionKey = new PartitionKey("Washington"),
MaxItemCount = 1000}))
{
// ...
}
При выполнении запроса полученные данные отправляются в пакете TCP. Если указано слишком маленькое значение для MaxItemCount, то число круговых путей, необходимых для отправки данных в пакете TCP, будет больше, что повлияет на производительность. Поэтому если вы не уверены, какое значение для свойства задано MaxItemCount , рекомендуется задать его значение -1 и позволить пакету SDK выбрать значение по умолчанию.
Настройка размера буфера
Параллельный запрос предназначен для предварительного получения результатов, пока текущий пакет результатов обрабатывается клиентом. Эта предварительная выборка помогает повысить общую задержку запроса. Свойство MaxBufferedItemCountQueryRequestOptions ограничивает количество предварительно подготовленных результатов. Задайте MaxBufferedItemCount ожидаемое количество возвращаемых результатов (или более высокое число), чтобы разрешить запросу получить максимальное преимущество от предварительной выборки. Если для этого значения задано значение -1, система автоматически определяет количество элементов для буфера.
using (FeedIterator<MyItem> feedIterator = container.GetItemQueryIterator<MyItem>(
"SELECT * FROM c WHERE c.city = 'Seattle'",
requestOptions: new QueryRequestOptions() {
PartitionKey = new PartitionKey("Washington"),
MaxBufferedItemCount = -1}))
{
// ...
}
Предварительная выборка работает одинаково независимо от степени параллелизма, и существует один буфер для данных из всех секций.
Следующие шаги
Дополнительные сведения о повышении производительности с помощью пакета SDK для .NET см. в следующих разделах:
Сократите количество вызовов плана запросов
Чтобы выполнить запрос, необходимо создать план запроса. Сетевые запросы к шлюзу Azure Cosmos DB увеличивают задержку операции запроса.
Использование кэширования плана запросов
План запроса для запроса, ограниченного одной секцией, кэшируется на клиенте. Это избавляет от необходимости обращаться к шлюзу, чтобы получить план запроса после первого обращения. Ключом для кэшированного плана запроса является строка запроса SQL. Необходимо убедиться, что запрос параметризован. Если нет, при поиске в кэше планов запросов часто происходит сбой, так как строки запросов вряд ли будут совпадать между собой в разных запросах. Кэширование плана запросов включено по умолчанию для пакета SDK для Java версии 4.20.0 и выше , а также для пакета SDK для Spring Data Azure Cosmos DB версии 3.13.0 и более поздней.
Используйте параметризованные одноразделные запросы
Для параметризованных запросов, которые ограничиваются ключом раздела с помощью setPartitionKey, и не содержат агрегатов (включая Distinct, DCount или Group By), можно обойтись без плана запроса:
CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
options.setPartitionKey(new PartitionKey("Washington"));
ArrayList<SqlParameter> paramList = new ArrayList<SqlParameter>();
paramList.add(new SqlParameter("@city", "Seattle"));
SqlQuerySpec querySpec = new SqlQuerySpec(
"SELECT * FROM c WHERE c.city = @city",
paramList);
// Sync API
CosmosPagedIterable<MyItem> filteredItems =
container.queryItems(querySpec, options, MyItem.class);
// Async API
CosmosPagedFlux<MyItem> filteredItems =
asyncContainer.queryItems(querySpec, options, MyItem.class);
Примечание.
Для запросов между разделами пакет SDK должен посетить все существующие разделы для проверки результатов. Чем более физических секций содержит контейнер, тем медленнее они потенциально могут быть.
Настройка степени параллелизма
Параллельные запросы позволяют одновременно обращаться к нескольким секциям. Однако данные из отдельного секционированного контейнера извлекаются последовательно в соответствии с запросом. Поэтому используйте параметр setMaxDegreeOfParallelism в CosmosQueryRequestOptions для установки значения, соответствующего числу секций. Если вы не знаете количество секций, просто укажите достаточно большое значение параметра setMaxDegreeOfParallelism. Система автоматически выберет минимальное из двух значений (количество секций или число, указанное пользователем) в качестве максимальной степени параллелизма. Установка значения -1 позволяет пакету SDK решить оптимальную параллельность.
Важно отметить, что параллельные запросы обеспечивают наилучшие преимущества, если данные равномерно распределяются по всем секциям в отношении запроса. Если секционированный контейнер секционирован таким образом, что все или большинство данных, возвращаемых запросом, сосредоточены в нескольких секциях (один раздел в худшем случае), то производительность запроса будет снижена.
CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
options.setPartitionKey(new PartitionKey("Washington"));
options.setMaxDegreeOfParallelism(-1);
// Define the query
// Sync API
CosmosPagedIterable<MyItem> filteredItems =
container.queryItems(querySpec, options, MyItem.class);
// Async API
CosmosPagedFlux<MyItem> filteredItems =
asyncContainer.queryItems(querySpec, options, MyItem.class);
Предположим, что:
- D = максимальное число параллельных задач по умолчанию (= общее количество процессоров на клиентском компьютере)
- P — определенное пользователем максимальное число параллельных задач;
- N — количество секций, которые необходимо посетить для получения ответов на запросы.
Ниже приведены последствия поведения параллельных запросов для разных значений P:
- (P == 0) => последовательный режим
- (P == 1) => не более одной задачи;
- (P > 1) => минимальное значение (P, N) параллельных задач;
- (P == -1) => минимум (N, D) параллельных задач.
Настройка размера страницы
При выполнении SQL-запроса если результирующий набор имеет слишком большой размер, результаты возвращаются в сегментированном виде. По умолчанию результаты возвращаются в пакетах по 100 элементов или 4 МБ, в зависимости от того, какое ограничение будет достигнуто первым. Увеличение размера страницы уменьшает количество необходимых обходов и повышает производительность запросов, возвращающих более 100 элементов. Если вы не уверены, какое значение нужно задать, 1000 обычно является хорошим выбором. Потребление памяти увеличивается по мере увеличения размера страницы, поэтому если рабочая нагрузка учитывает память, рассмотрите более низкое значение.
Чтобы задать размер страницы, можно использовать параметр pageSize в iterableByPage() для синхронного API и параметр byPage() для асинхронного API:
// Sync API
Iterable<FeedResponse<MyItem>> filteredItemsAsPages =
container.queryItems(querySpec, options, MyItem.class).iterableByPage(continuationToken,pageSize);
for (FeedResponse<MyItem> page : filteredItemsAsPages) {
for (MyItem item : page.getResults()) {
//...
}
}
// Async API
Flux<FeedResponse<MyItem>> filteredItemsAsPages =
asyncContainer.queryItems(querySpec, options, MyItem.class).byPage(continuationToken,pageSize);
filteredItemsAsPages.map(page -> {
for (MyItem item : page.getResults()) {
//...
}
}).subscribe();
Настройка размера буфера
Параллельный запрос предназначен для предварительного получения результатов, пока текущий пакет результатов обрабатывается клиентом. Предварительная выборка помогает уменьшить общую задержку запроса.
setMaxBufferedItemCount ограничивает CosmosQueryRequestOptions количество предварительно выборанных результатов. Чтобы максимально увеличить предварительную выборку, установите значение maxBufferedItemCount выше значения pageSize (ПРИМЕЧАНИЕ. Это также может привести к большому потреблению памяти). Чтобы свести к минимуму предварительную выборку, установите maxBufferedItemCount равным pageSize. Если для этого значения задано значение 0, система автоматически определяет количество элементов для буфера.
CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
options.setPartitionKey(new PartitionKey("Washington"));
options.setMaxBufferedItemCount(-1);
// Define the query
// Sync API
CosmosPagedIterable<MyItem> filteredItems =
container.queryItems(querySpec, options, MyItem.class);
// Async API
CosmosPagedFlux<MyItem> filteredItems =
asyncContainer.queryItems(querySpec, options, MyItem.class);
Предварительная выборка работает одинаково независимо от степени параллелизма, и существует один буфер для данных из всех секций.
Следующие шаги
Дополнительные сведения о повышении производительности с помощью пакета SDK для Java см. в следующих разделах:
Сократите количество вызовов плана запросов
Чтобы выполнить запрос, необходимо создать план запроса. Сетевые запросы к шлюзу Azure Cosmos DB увеличивают задержку операции запроса. Существует способ удалить этот запрос и уменьшить задержку операции запроса для одного раздела. Для одиночных запросов по разделу укажите значение ключа раздела для элемента и передайте его как аргумент partition_key.
items = container.query_items(
query="SELECT * FROM r where r.city = 'Seattle'",
partition_key="Washington"
)
Настройка размера страницы
При выполнении SQL-запроса если результирующий набор имеет слишком большой размер, результаты возвращаются в сегментированном виде. Max_item_count позволяет задать максимальное количество элементов, возвращаемых в операции перечисления.
items = container.query_items(
query="SELECT * FROM r where r.city = 'Seattle'",
partition_key="Washington",
max_item_count=1000
)
Следующие шаги
Дополнительные сведения об использовании пакета SDK Python для API для NoSQL:
Сократите количество вызовов плана запросов
Чтобы выполнить запрос, необходимо создать план запроса. Сетевые запросы к шлюзу Azure Cosmos DB увеличивают задержку операции запроса. Существует способ исключить этот запрос и снизить задержку операции запроса с одной секцией. Для однораздельных запросов можно сузить запрос до одного раздела двумя способами.
Использование параметризованного выражения запроса и указание ключа секции в инструкции запроса. Запрос создается программно:SELECT * FROM todo t WHERE t.partitionKey = 'Bikes, Touring Bikes'
// find all items with same categoryId (partitionKey)
const querySpec = {
query: "select * from products p where p.categoryId=@categoryId",
parameters: [
{
name: "@categoryId",
value: "Bikes, Touring Bikes"
}
]
};
// Get items
const { resources } = await container.items.query(querySpec).fetchAll();
for (const item of resources) {
console.log(`${item.id}: ${item.name}, ${item.sku}`);
}
Или укажите partitionKey и передайте его в FeedOptions качестве аргумента:
const querySpec = {
query: "select * from products p"
};
const { resources } = await container.items.query(querySpec, { partitionKey: "Bikes, Touring Bikes" }).fetchAll();
for (const item of resources) {
console.log(`${item.id}: ${item.name}, ${item.sku}`);
}
Настройка размера страницы
При выполнении SQL-запроса если результирующий набор имеет слишком большой размер, результаты возвращаются в сегментированном виде. MaxItemCount позволяет задать максимальное количество элементов, возвращаемых в операции перечисления.
const querySpec = {
query: "select * from products p where p.categoryId=@categoryId",
parameters: [
{
name: "@categoryId",
value: items[2].categoryId
}
]
};
const { resources } = await container.items.query(querySpec, { maxItemCount: 1000 }).fetchAll();
for (const item of resources) {
console.log(`${item.id}: ${item.name}, ${item.sku}`);
}
Улучшенное управление запросами
Для пакета SDK JS для Cosmos DB версии 4.3.0 и более поздних версий был представлен флаг, enableQueryControl обеспечивающий больший контроль над выполнением запросов, что обеспечивает большую гибкость в управлении потреблением единиц запросов (ЕЗ).
По умолчанию enableQueryControl установлено в false, и пакет SDK гарантирует, что каждый вызов fetchNext вернет maxItemCount количество результатов, если такое количество результатов имеется на сервере. Но для удовлетворения гарантированного количества результатов, SDK может запрашивать разделы на серверной стороне несколько раз в одной fetchNext итерации. Иногда это может привести к непредсказуемой задержке и снижению использования ресурсов без пользовательского управления, особенно если результаты распределены по разделам.
Если для enableQueryControl установлено значение true, каждый вызов fetchNext теперь запрашивает до maxDegreeOfParallelism физических разделов. Если результаты не найдены или результаты пока не готовы к работе, пакет SDK возвращает пустые страницы вместо продолжения поиска всех разделов в фоновом режиме. Таким образом, пользователи получают более точный контроль над выполнением запроса с предсказуемой задержкой и детализированными данными потребления RU.
const options = {
enableQueryControl: true, // Flag to enable new query pipeline.
maxItemCount: 100,
maxDegreeOfParallelism: 6
};
const querySpec = {
query: "select * from products p where p.categoryId=@categoryId",
parameters: [
{
name: "@categoryId",
value: items[2].categoryId
}
]
};
const queryIterator = container.items.query(querySpec, options);
// use this iterator to fetch results.
Следующие шаги
Дополнительные сведения об использовании пакета SDK Node.js для API для NoSQL: