Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL
В этой статье рассматривается общий рекомендуемый подход для устранения проблем с запросами в Azure Cosmos DB. Хотя не следует думать, что действия, описанные в этой статье, дают полную защиту от потенциальных проблем с запросами, мы включили сюда наиболее распространенные советы по повышению производительности. Эту статью следует использовать в качестве отправного места для устранения неполадок медленных или дорогостоящих запросов в Azure Cosmos DB для NoSQL. Можно также использовать журналы диагностики для поиска запросов, которые выполняются слишком долго или потребляют значительный объем пропускной способности. Если вы используете API Azure Cosmos DB для выполнения запросов MongoDB, вам пригодится это руководство.
Оптимизации запросов в Azure Cosmos DB распределены по категориям следующим образом.
- Оптимизация, снижающая расход единиц запроса (RU) в ходе выполнения запроса.
- Оптимизация, которая просто сокращает задержку.
Если вы уменьшите стоимость запроса в RU, вы обычно также уменьшите задержку.
Прежде чем приступить к работе с этим руководством, рекомендуется рассмотреть распространенные проблемы с пакетом SDK, не связанные с обработчиком запросов.
- Следуйте этим рекомендациям по повышению производительности пакета SDK при работе с запросами.
- Иногда запросы могут содержать пустые страницы, даже если на следующей странице есть результаты. Причины этого могут быть:
- Пакет SDK может выполнять несколько сетевых вызовов.
- Для получения документов запросу может требоваться много времени.
- Все запросы имеют маркер продолжения, позволяющий продолжить запрос. Не забудьте полностью очистить запрос. Дополнительные сведения об обработке нескольких страниц результатов
При оптимизации запроса в Azure Cosmos DB первым шагом всегда является получение метрик запроса для запроса. Эти метрики доступны также на портале Azure. После выполнения запроса в обозреватель данных метрики запроса отображаются рядом с вкладкой Результаты:
Получив метрики запроса, сравните Число полученных документов с Числом выходных документов для запроса. Используйте это сравнение, чтобы определить соответствующие разделы для проверки в этой статье.
Число полученных документов — это число документов, которые нужно было загрузить подсистеме обработки запросов. Число выходных документов — число документов, которые были необходимы для результатов запроса. Если число извлеченных документов выше, чем число выходных документов, была по крайней мере одна часть вашего запроса, которая не смогла использовать индекс и нуждалась в сканировании.
Ознакомьтесь со следующими разделами, чтобы получить представление об актуальных для вашего сценариях оптимизациях запросов.
Повысьте эффективность выполнения строковых системных функций.
Оптимизируйте запросы, имеющие как фильтр, так и предложение ORDER BY (упорядочить по).
Число полученных документов — это число документов, которые нужно было загрузить подсистеме обработки запросов. Число выходных документов — это число документов, возвращаемых запросом. Если число извлеченных документов выше числа выходных документов, значит, была по крайней мере одна часть вашего запроса, которая не смогла использовать индекс и нужно было выполнить сканирование.
Ниже приведен пример запроса сканирования, который не был полностью обработан индексом.
Запрос:
SELECT VALUE c.description
FROM c
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Метрики запроса:
Retrieved Document Count : 60,951
Retrieved Document Size : 399,998,938 bytes
Output Document Count : 7
Output Document Size : 510 bytes
Index Utilization : 0.00 %
Total Query Execution Time : 4,500.34 milliseconds
Query Preparation Times
Query Compilation Time : 0.09 milliseconds
Logical Plan Build Time : 0.05 milliseconds
Physical Plan Build Time : 0.04 milliseconds
Query Optimization Time : 0.01 milliseconds
Index Lookup Time : 0.01 milliseconds
Document Load Time : 4,177.66 milliseconds
Runtime Execution Times
Query Engine Times : 322.16 milliseconds
System Function Execution Time : 85.74 milliseconds
User-defined Function Execution Time : 0.00 milliseconds
Document Write Time : 0.01 milliseconds
Client Side Metrics
Retry Count : 0
Request Charge : 4,059.95 RUs
Число извлеченных документов (60 951) выше, чем число выходных документов (7), что означает, что этот запрос привел к сканированию документов. В этом случае системная функция UPPER () не использует индекс.
Политика индексирования должна охватывать все свойства, входящие в предложения WHERE
, предложения ORDER BY
, JOIN
и большинство системных функций. Требуемые пути, указанные в политике индекса, должны соответствовать свойствам в документах JSON.
Примечание
Свойства в политике индексирования Azure Cosmos DB регистрозависимы.
Запрос:
SELECT *
FROM c
WHERE c.description = "Malabar spinach, cooked"
Политика индексации.
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*"
}
],
"excludedPaths": [
{
"path": "/description/*"
}
]
}
Стоимость в ЕЗ: 409,51 ЕЗ
обновленная политика индексации.
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*"
}
],
"excludedPaths": []
}
Стоимость в РУ: 2,98 РУ
Вы можете в любое время добавлять свойства в политику индексации без влияния на доступность операций чтений и записи. Можно отслеживать ход преобразования индекса.
Большинство системных функций используют индексы. Вот список некоторых распространенных строковых функций, которые применяют индексы:
- StartsWith
- Содержит
- RegexMatch
- Лево
- Substring (но только если первый параметр num_expr равен 0)
Ниже приведены некоторые распространенные системные функции, которые не используют индекс и должны загружать каждый документ в случае использования в предложении WHERE
:
Системная функция | Идеи по оптимизации |
---|---|
Верхний/Нижний | Вместо использования системной функции для нормализации данных при сравнениях следует нормализировать регистр при вставке. Запрос, подобный SELECT * FROM c WHERE UPPER(c.name) = 'BOB' , преобразуется в SELECT * FROM c WHERE c.name = 'BOB' . |
GetCurrentDateTime/GetCurrentTimestamp/GetCurrentTicks | Определите текущее время перед выполнением запроса и используйте это строковое значение в предложении WHERE . |
Математические функции (не являющиеся агрегатными функциями) | Если в запросе необходимо часто вычислять значение, рекомендуется сохранить значение как свойство в документе JSON. |
Эти системные функции могут использовать индексы за исключением случаев, когда они включены в запросы со статистическими выражениями:
Системная функция | Идеи по оптимизации |
---|---|
Пространственные системные функции | Сохраните результат запроса в материализованном представлении в реальном времени. |
При использовании в предложении SELECT
неэффективные системные функции не влияют на то, как индексы используются запросами.
Для некоторых системных функций, которые используют индексы, вы можете добавить в запрос предложение ORDER BY
, чтобы улучшить его выполнение.
В частности, для любой системной функции, для которой стоимость в ЕЗ увеличивается по мере увеличения кратности свойства, в запрос имеет смысл включить предложение ORDER BY
. Такие запросы выполняют сканирование индекса, поэтому упорядочение результатов запроса может сделать его более эффективным.
Такая оптимизация позволяет улучшить выполнение следующих системных функций:
- StartsWith (с параметром case-insensitive = true)
- StringEquals (с параметром case-insensitive = true)
- Содержит
- RegexMatch
- EndsWith
Например, рассмотрим SQL-запрос ниже с предложением CONTAINS
.
CONTAINS
будет использовать индексы, но иногда даже после добавления соответствующего индекса вы все равно можете заметить высокое потребление ресурсов при выполнении следующего запроса.
Исходный запрос:
SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
Чтобы улучшить выполнение запроса, добавьте ORDER BY
:
SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
ORDER BY c.town
Такая же оптимизация может помочь в запросах с другими фильтрами. В этом случае лучше также добавлять в предложение ORDER BY
свойства с фильтрами равенства.
Исходный запрос:
SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
Чтобы улучшить выполнение запроса, добавьте ORDER BY
и составной индекс для (c.name, c.town):
SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
ORDER BY c.name, c.town
В большинстве случаев агрегатные системные функции в Azure Cosmos DB используют индекс. Однако в зависимости от фильтров или других предложений в агрегатном запросе обработчик запросов может потребоваться для загрузки большого количества документов. Как правило, обработчик запросов сначала применяет фильтры равенства и диапазона. После применения этих фильтров подсистема запросов может оценить другие фильтры и прибегнуть к загрузке оставшихся документов для вычисления агрегата при необходимости.
Например, учитывая эти два примера запросов, запрос с равенством и CONTAINS
фильтром системных функций, как правило, эффективнее, чем запрос с только системным фильтром CONTAINS
функций. Это обусловлено тем, что фильтр равенства применяется в первую очередь и использует индекс до того, как документы нужно будет загрузить для более затратного фильтра CONTAINS
.
Запрос только с фильтром CONTAINS
- более высокая стоимость RU-единиц:
SELECT COUNT(1)
FROM c
WHERE CONTAINS(c.description, "spinach")
Запрос с фильтром равенства и фильтром CONTAINS
— сниженная плата за использование RU:
SELECT AVG(c._ts)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats" AND CONTAINS(c.description, "spinach")
Ниже приведены дополнительные примеры агрегатных запросов, которые не будут полностью использовать индекс:
Чтобы узнать, использует ли запрос индекс, необходимо обратиться к странице соответствующей системной функции.
SELECT MAX(c._ts)
FROM c
WHERE CONTAINS(c.description, "spinach")
SELECT AVG(c._ts)
FROM c
WHERE udf.MyUDF("Sausages and Luncheon Meats")
Объем ресурсных единиц (RU) для запросов с GROUP BY
увеличивается по мере роста количества свойств в GROUP BY
предложении. Например, в приведенном ниже запросе расход RU увеличивается по мере увеличения числа уникальных описаний.
Плата в очках производительности (RU) за агрегатную функцию с предложением GROUP BY
выше, чем плата за саму по себе агрегатную функцию. В этом примере движок запросов должен загрузить каждый документ, соответствующий фильтру c.foodGroup = "Sausages and Luncheon Meats"
, поэтому ожидается высокая плата.
SELECT COUNT(1)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats"
GROUP BY c.description
Если планируется часто выполнять одни и те же статистические запросы, может оказаться более эффективным создание материализованных представлений в реальном времени с каналом изменений Azure Cosmos DB, чем выполнение отдельных запросов.
Хотя запросы с фильтром и ORDER BY
предложением обычно используют индекс диапазона, они более эффективны, если могут обслуживаться составным индексом. В дополнение к изменению политики индексации необходимо добавить все свойства из составного индекса в предложение ORDER BY
. Это изменение запроса гарантирует, что он использует составной индекс.
Запрос:
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c._ts ASC
Политика индексации.
{
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
{
"path":"/*"
}
],
"excludedPaths":[]
}
Стоимость в RU: 44,28 RU
Обновленный запрос (включает в себя оба свойства из предложения ORDER BY
):
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c.foodGroup, c._ts ASC
обновленная политика индексации.
{
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
{
"path":"/*"
}
],
"excludedPaths":[],
"compositeIndexes":[
[
{
"path":"/foodGroup",
"order":"ascending"
},
{
"path":"/_ts",
"order":"ascending"
}
]
]
}
Плата в RU: 8,86 RU
Вложенные запросы с несколькими значениями могут оптимизировать выражения JOIN
за счёт размещения предикатов после каждого выражения select-many, а не после всех перекрёстных соединений в клаузе WHERE
.
Рассмотрим следующий запрос:
SELECT Count(1) AS Count
FROM c
JOIN t IN c.tags
JOIN n IN c.nutrients
JOIN s IN c.servings
WHERE t.name = 'infant formula' AND (n.nutritionValue > 0
AND n.nutritionValue < 10) AND s.amount > 1
Стоимость в ЕЗ: 167,62 ЕЗ
Для этого запроса индекс соответствует любому документу с тегом с именем infant formula
, nutritionValue
больше 0 и amount
больше 1. Выражение здесь JOIN
выполняет перекрестное произведение всех элементов тегов, питательных веществ и обслуживает массивы для каждого соответствующего документа перед применением любого фильтра. Затем предложение WHERE
применит предикат фильтра к каждому кортежу <c, t, n, s>
.
Например, если соответствующий документ содержит 10 элементов в каждом из трех массивов, он расширяется до 1 x 10 x 10 x 10 x 10 (то есть 1000) кортежей. Использование вложенных запросов здесь может помочь отфильтровать присоединенные элементы массива перед присоединением к следующему выражению.
Этот запрос эквивалентен предыдущему, но использует вложенные запросы:
SELECT Count(1) AS Count
FROM c
JOIN (SELECT VALUE t FROM t IN c.tags WHERE t.name = 'infant formula')
JOIN (SELECT VALUE n FROM n IN c.nutrients WHERE n.nutritionValue > 0 AND n.nutritionValue < 10)
JOIN (SELECT VALUE s FROM s IN c.servings WHERE s.amount > 1)
Плата в RU: 22,17 RU
Предположим, что только один элемент в массиве тегов соответствует фильтру и в нем есть пять элементов для массивов питательных веществ и порций. Выражения JOIN
расширяются до 1 x 1 x 1 x 5 x 5 = 25 элементов, а не до 1000 элементов в первом запросе.
Если Число полученных документов приблизительно равно Числу выходных документов, механизму запросов не нужно было сканировать много ненужных документов. Для многих запросов, таких как использование ключевого слова TOP
, Число полученных документов может превысить Число выходных документов на 1. Вам не нужно беспокоиться об этом.
Azure Cosmos DB использует секционирование для масштабирования отдельных контейнеров по мере роста числа единиц запросов и потребностей хранилища данных. Каждый физический раздел имеет отдельный и независимый индекс. Если запрос имеет фильтр равенства, соответствующий ключу секции контейнера, необходимо проверить только индекс соответствующей секции. Эта оптимизация сокращает общее число единиц обработки, необходимых для запроса.
При наличии большого количества подготовленных единиц запроса (более 30 000) или большого объема хранимых данных (более чем примерно 100 ГБ) контейнер, вероятно, будет достаточно велик, чтобы увидеть значительное стоимости в единицах запроса.
Например, если вы создаете контейнер с разделом key foodGroup, следующие запросы должны проверить только одну физическую секцию:
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"
Запросы, имеющие фильтр по ключу раздела IN
, проверяют только одну или несколько соответствующих физических разделов и не будут выполняться параллельно.
SELECT *
FROM c
WHERE c.foodGroup IN("Soups, Sauces, and Gravies", "Vegetables and Vegetable Products") and c.description = "Mushroom, oyster, raw"
Запросы, имеющие фильтры диапазонов в ключе секции или не имеющие фильтров по ключу секции, вынуждены будут "растекаться" и проверять наличие результатов в индексах всех физических секций:
SELECT *
FROM c
WHERE c.description = "Mushroom, oyster, raw"
SELECT *
FROM c
WHERE c.foodGroup > "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"
Хотя запросы, имеющие фильтры по нескольким свойствам, обычно будут использовать индекс диапазона, они более эффективны, если их можно обслуживать из составного индекса. Для небольших объемов данных такая оптимизация не окажет существенного влияния. Однако она может быть полезна для больших объемов данных. Для одного составного индекса можно оптимизировать только один фильтр неравенства. Если в запросе есть несколько фильтров без проверки на равенство, выберите один из них для использования составного индекса. Остальные продолжают использовать индексы диапазона. Фильтр неравенства должен быть определён последним в составном индексе. Дополнительные сведения о составных индексах.
Ниже приведены некоторые примеры запросов, которые можно оптимизировать с помощью составного индекса.
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts = 1575503264
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts > 1575503264
Вот соответствующий составной индекс:
{
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
{
"path":"/*"
}
],
"excludedPaths":[],
"compositeIndexes":[
[
{
"path":"/foodGroup",
"order":"ascending"
},
{
"path":"/_ts",
"order":"ascending"
}
]
]
}
Часто стоимость в единицах RU может быть приемлемой, даже когда задержка запроса все еще слишком высока. В следующих разделах представлен обзор советов по сокращению задержки запросов. Если вы запускаете один и тот же запрос несколько раз на одном и том же наборе данных, заряд в единицах RU будет, как правило, одинаковым каждый раз. Однако задержка запросов может варьироваться между выполнениями запросов.
Запросы, выполняемые из другого региона, отличного от учетной записи Azure Cosmos DB, имеют более высокую задержку, чем если бы они выполнялись в одном регионе. Например, если выполнять код на своем настольном компьютере, задержка повысится на десятки и сотни миллисекунд (если не больше), по сравнению с запросом, поступившим с виртуальной машины в том же регионе Azure, что и Azure Cosmos DB. Можно легко глобально распределить данные в Azure Cosmos DB, чтобы ваши данные находились ближе к вашему приложению.
В Azure Cosmos DB подготовленная пропускная способность измеряется в единицах запроса (ЕЗ). Представьте, что у вас есть запрос, который использует 5 RU пропускной способности. Например, если вы выделяете 1000 ЕЗ, вы сможете выполнять этот запрос 200 раз в секунду. Если вы попытались выполнить запрос при недостаточной пропускной способности, Azure Cosmos DB возвратит ошибку HTTP 429. Любой из текущих API для NoSQL SDK автоматически повторит этот запрос после короткой задержки. Ограниченные запросы занимают больше времени, поэтому увеличение выделенной пропускной способности может улучшить задержку выполнения запросов. Вы можете просмотреть общее число регулируемых запросов в колонке Метрики портала Azure.
Параллельные запросы позволяют одновременно обращаться к нескольким секциям. Но данные из каждой секционированной коллекции извлекаются в рамках запроса последовательно. Таким образом, установка параметра MaxConcurrency на число секций позволит достичь высокой производительности запроса, если все другие состояния системы остаются неизменными. Если количество разделов неизвестно, можно установить MaxConcurrency (или MaxDegreesOfParallelism в более старых версиях SDK) на большое значение. Система выбирает минимальное (количество секций, предоставленных пользователем входных данных) в качестве максимальной степени параллелизма.
Запросы предназначены для предварительного получения результатов, пока текущий пакет результатов обрабатывается клиентом. Предварительная выборка способствует общему уменьшению задержки при обработке запроса. Значение setMaxBufferedItemCount ограничивает количество предварительно выбираемых результатов. Если установить этот параметр на ожидаемое число возвращаемых результатов (или большее число), запрос может получить наибольшее преимущество от предварительной выборки. Если установить это значение на –1, система автоматически определит количество элементов для буферизации.
Ознакомьтесь со следующими статьями, чтобы получить сведения о том, как измерять единицы запросов (RUs) для каждого запроса, получать статистику выполнения для оптимизации запросов и другой полезной информацией.