Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Область применения:
NoSQL
Mongodb
Кассандра
Гремлин
Таблица
Azure Cosmos DB — это база данных без использования схем, которая позволяет выполнять итерацию приложения, не отвлекаясь на управление схемами или индексами. По умолчанию Azure Cosmos DB автоматически индексирует каждое свойство всех элементов в контейнере. Разработчикам не нужно определять схемы или настраивать вторичные индексы.
В этой статье объясняется, как azure Cosmos DB индексирует данные и как он использует индексы для повышения производительности запросов. Перед изучением настройки политик индексирования рекомендуется ознакомиться с этим разделом.
От элементов к деревьям
Каждый раз, когда элемент сохраняется в контейнере, его содержимое проецируется как документ JSON, а затем преобразуется в представление в виде дерева. Это преобразование означает, что каждое свойство этого элемента представляется как узел в дереве. Псевдокорневой узел создается как родитель для всех свойств элемента первого уровня. Листовые узлы содержат фактические скалярные значения, перенесенные элементом.
Рассмотрим этот элемент в качестве примера:
{
"locations": [
{ "country": "Germany", "city": "Berlin" },
{ "country": "France", "city": "Paris" }
],
"headquarters": { "country": "Belgium", "employees": 250 },
"exports": [
{ "city": "Moscow" },
{ "city": "Athens" }
]
}
Это дерево представляет пример элемента JSON:
Обратите внимание, как массивы кодируются в дереве: каждая запись в массиве получает промежуточный узел с меткой индекса этой записи в массиве (0, 1 и т. д.).
От деревьев к путям к свойствам
Azure Cosmos DB преобразует элементы в деревья, так как позволяет системе ссылаться на свойства с помощью путей в этих деревьях. Чтобы получить путь к свойству, мы можем перемещаться по дереву с корневого узла к этому свойстве и объединять метки каждого пройденного узла.
Ниже приведены пути для каждого свойства из примера элемента, описанного ранее:
-
/locations/0/country
: "Германия" -
/locations/0/city
: "Берлин" -
/locations/1/country
: "Франция" -
/locations/1/city
: "Париж" -
/headquarters/country
: "Бельгия" -
/headquarters/employees
: 250 -
/exports/0/city
: "Москва" -
/exports/1/city
: "Афины"
Azure Cosmos DB эффективно индексирует путь каждого свойства и соответствующее значение при записи элемента.
Типы индексов
В настоящее время Azure Cosmos DB поддерживает три типа индексов. Такие типы индексов можно настроить при определении политики индексации.
Индекс диапазона
Индексы диапазона основаны на упорядоченной структуре дерева. Индексы диапазонов используются в следующих случаях:
Запросы о равенстве
SELECT * FROM container c WHERE c.property = 'value'
SELECT * FROM c WHERE c.property IN ("value1", "value2", "value3")
совпадение для элемента массива
SELECT * FROM c WHERE ARRAY_CONTAINS(c.tags, "tag1")
Запросы к диапазону:
SELECT * FROM container c WHERE c.property > 'value'
Примечание.
Работает для
>
,<
,>=
,<=
,!=
Проверка наличия свойства:
SELECT * FROM c WHERE IS_DEFINED(c.property)
Строковые системные функции:
SELECT * FROM c WHERE CONTAINS(c.property, "value")
SELECT * FROM c WHERE STRINGEQUALS(c.property, "value")
Запросы
ORDER BY
:SELECT * FROM container c ORDER BY c.property
Запросы
JOIN
:SELECT child FROM container c JOIN child IN c.properties WHERE child = 'value'
Индексы диапазона можно использовать для скалярных значений (строка или число). Политика индексирования по умолчанию, задаваемая для только что созданных контейнеров, применяет диапазонные индексы для любых строк или чисел. Сведения о настройке индексов диапазона см. в статье "Управление политиками индексирования" в Azure Cosmos DB.
Примечание.
Предложение ORDER BY
, которое упорядочивает одно свойство , всегда нуждается в индексе диапазона и завершается ошибкой, если путь, на который он ссылается, не имеет одного. Аналогичным образом запрос ORDER BY
, который упорядочивает по нескольким свойствам, всегда нуждается в составном индексе.
Пространственный индекс
Пространственные индексы обеспечивают эффективные запросы для геопространственных объектов, таких как точки, линии, полигоны и многополигионы. Эти запросы используют , ST_DISTANCE
ST_WITHIN
ST_INTERSECTS
ключевые слова. Ниже приведены некоторые примеры, которые используют пространственные индексы:
Геопространственные запросы расстояний:
SELECT * FROM container c WHERE ST_DISTANCE(c.property, { "type": "Point", "coordinates": [0.0, 10.0] }) < 40
Геопространственные запросы с условием "в пределах"
SELECT * FROM container c WHERE ST_WITHIN(c.property, {"type": "Point", "coordinates": [0.0, 10.0] })
Запросы на пересечение геопространственных данных:
SELECT * FROM c WHERE ST_INTERSECTS(c.property, { 'type':'Polygon', 'coordinates': [[ [31.8, -5], [32, -5], [31.8, -5] ]] })
Пространственные индексы можно использовать для правильно отформатированных объектов GeoJSON. В настоящее время поддерживаются Points, LineStrings, Polygons и MultiPolygons. Сведения о настройке пространственных индексов см. в статье "Управление политиками индексирования в Azure Cosmos DB".
Составные индексы
Составные индексы повышают эффективность при выполнении операций с несколькими полями. Составные индексы используются в таких случаях:
Запросы
ORDER BY
к нескольким свойствам:SELECT * FROM container c ORDER BY c.property1, c.property2
Запросы с фильтром и
ORDER BY
. Эти запросы могут использовать составной индекс, если свойство фильтра добавлено в предложениеORDER BY
.SELECT * FROM container c WHERE c.property1 = 'value' ORDER BY c.property1, c.property2
Запросы с фильтром по двум или нескольким свойствам, где по крайней мере одно свойство является фильтром равенства:
SELECT * FROM container c WHERE c.property1 = 'value' AND c.property2 > 'value'
Если один предикат фильтра использует один из типов индексов, обработчик запросов оценивает это сначала перед сканированием остальной части. Например, если у вас есть SQL-запрос, например SELECT * FROM c WHERE c.firstName = "Andrew" and CONTAINS(c.lastName, "Liu")
:
Этот запрос сначала фильтрует записи, в которых firstName = "Andrew" с помощью индекса. Затем он передает все записи firstName = Andrew через последующий конвейер для оценки предиката фильтра CONTAINS.
Вы можете ускорить запросы и избежать полного сканирования контейнеров при использовании функций, выполняющих полную проверку, например CONTAINS. Вы можете добавить дополнительные предикаты фильтров, которые используют индекс для ускорения этих запросов. Порядок условий фильтра не имеет значения. Модуль запросов определяет, какие предикаты являются более выборочными и выполняют запрос соответствующим образом.
Сведения о настройке составных индексов см. в статье "Управление политиками индексирования в Azure Cosmos DB".
Векторные индексы
Индексы векторов повышают эффективность при выполнении векторного поиска с помощью системной VectorDistance
функции. Поиски векторов имеют значительно более низкую задержку, более высокую пропускную способность и меньшее потребление единиц ресурсов при использовании векторного индекса. Azure Cosmos DB для NoSQL поддерживает любые векторные встраивания (текст, изображение, мультимодальные и т. д.) размером до 4096 измерений.
Сведения о настройке векторных индексов см. в примерах политики индексирования векторов.
ORDER BY
запросы векторного поиска:SELECT TOP 10 * FROM c ORDER BY VectorDistance(c.vector1, c.vector2)
Проекция оценки сходства в векторных поисковых запросах:
SELECT TOP 10 c.name, VectorDistance(c.vector1, c.vector2) AS SimilarityScore FROM c ORDER BY VectorDistance(c.vector1, c.vector2)
Диапазонные фильтры по оценке сходства.
SELECT TOP 10 * FROM c WHERE VectorDistance(c.vector1, c.vector2) > 0.8 ORDER BY VectorDistance(c.vector1, c.vector2)
Внимание
В настоящее время политики векторов и векторные индексы неизменяемы после создания. Чтобы внести изменения, создайте новую коллекцию.
Использование индексов
Существует пять способов, с помощью которых механизм запросов может оценивать фильтры запросов, отсортированные от наиболее эффективных до наименее эффективных.
- Поиск в индексе
- Точная проверка индексов
- Расширенная проверка индекса
- Полное сканирование индекса
- Полная проверка
При индексировании путей свойств обработчик запросов автоматически использует индекс как можно эффективнее. Помимо индексирования новых путей свойств, для оптимизации того, как запросы используют индекс, не нужно настраивать что-либо еще. RU-начисление за запрос является сочетанием RU-начисления за использование индекса и RU-начисления при загрузке элементов.
В следующей таблице приведены различные способы использования индексов в Azure Cosmos DB:
Тип поиска индекса | Описание | Распространенные примеры | Сбор за использование индекса RU | Плата за загрузку элементов из транзакционного хранилища данных |
---|---|---|---|---|
Поиск в индексе | Чтение только необходимых индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | Фильтры равенства, IN | Константа для фильтра равенства | Увеличивается в зависимости от количества элементов в результатах запроса |
Точная проверка индексов | Двоичный поиск индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | Сравнения диапазонов (>, <, <=, или >=), StartsWith | Сравнимо с поиском по индексу, увеличение слегка зависит от кардинальности индексируемых свойств. | Увеличивается в зависимости от количества элементов в результатах запроса |
Расширенная проверка индекса | Оптимизированный поиск (но менее эффективный, чем двоичный поиск) индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | StartsWith (без учета регистра), StringEquals (без учета регистра) | Незначительно увеличивается в зависимости от кардинальности индексированных свойств | Увеличивается в зависимости от количества элементов в результатах запроса |
Полное сканирование индекса | Чтение конкретного набора индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | Содержит, EndsWith, RegexMatch, LIKE | Увеличивается линейно в зависимости от количества элементов индексированных свойств | Увеличивается в зависимости от количества элементов в результатах запроса |
Полная проверка | Загрузка всех элементов из хранилища транзакционных данных | Верхний, Нижний | Н/П | Увеличивается в зависимости от числа элементов в контейнере |
При написании запросов следует использовать предикаты фильтров, которые используют индекс максимально эффективно. Например, если для вашего варианта использования подойдет либо StartsWith
, либо Contains
, вам следует выбрать StartsWith
, так как он выполняет точное сканирование индекса вместо полного сканирования индекса.
Данные об использовании индекса
В этом разделе рассматриваются дополнительные сведения о том, как запросы используют индексы. Этот уровень детализации не требуется, чтобы узнать, как приступить к работе с Azure Cosmos DB, но подробно описан для любопытных пользователей. Мы ссылаемся на пример элемента, доступного ранее в этом документе:
Элементы для примера.
{
"id": 1,
"locations": [
{ "country": "Germany", "city": "Berlin" },
{ "country": "France", "city": "Paris" }
],
"headquarters": { "country": "Belgium", "employees": 250 },
"exports": [
{ "city": "Moscow" },
{ "city": "Athens" }
]
}
{
"id": 2,
"locations": [
{ "country": "Ireland", "city": "Dublin" }
],
"headquarters": { "country": "Belgium", "employees": 200 },
"exports": [
{ "city": "Moscow" },
{ "city": "Athens" },
{ "city": "London" }
]
}
Azure Cosmos DB использует инвертированный индекс. Индекс работает путем сопоставления каждого пути JSON с набором элементов, содержащих это значение. Сопоставление идентификаторов элементов представлено на множестве разных страниц индекса для контейнера. Ниже приведен пример схемы инвертированного индекса для контейнера, включающего два примера элементов:
Путь | Значение | Список идентификаторов элементов |
---|---|---|
/locations/0/country | Германия | 1 |
/locations/0/country | Ирландия | 2 |
/locations/0/city | Берлин | 1 |
/locations/0/city | Дублин | 2 |
/locations/1/страна | Франция | 1 |
/locations/1/city | Париж | 1 |
/штаб-квартира/страна | Бельгия | 1, 2 |
/headquarters/employees | 200 | 2 |
/headquarters/employees | 250 | 1 |
Инвертированный индекс имеет два важных атрибута.
- Для данного пути значения сортируются в возрастающем порядке. Таким образом, обработчик запросов может легко извлекать
ORDER BY
из индекса. - Для заданного пути поисковый движок может сканировать через различный набор возможных значений, чтобы определить страницы индекса, на которых имеются результаты.
Обработчик запросов может использовать инвертированный индекс четырьмя разными способами.
Поиск в индексе
Обратите внимание на следующий запрос:
SELECT location
FROM location IN company.locations
WHERE location.country = 'France'
Предикат запроса (фильтрация по элементам, где любое расположение имеет "Франция" в качестве страны или региона) будет соответствовать пути, выделенному на этой схеме:
Поскольку этот запрос имеет фильтр равенства, после обхода этого дерева можно быстро определить страницы индекса, содержащие результаты запроса. В этом случае обработчик запросов будет считывать страницы индекса, содержащие элемент 1. Поиск по индексу является наиболее эффективным способом использования индекса. При поиске индекса мы считываем только необходимые страницы индекса и загружаем только элементы в результатах запроса. Таким образом, время поиска по индексу и расходы в RU за поиск по индексу чрезвычайно низкие, независимо от общего объема данных.
Точная проверка индексов
Обратите внимание на следующий запрос:
SELECT *
FROM company
WHERE company.headquarters.employees > 200
Предикат запроса (фильтрация по элементам, в которых имеется более 200 сотрудников) можно оценить с помощью точного просмотра индекса пути headquarters/employees
. При выполнении точной проверки индекса обработчик запросов начинает с двоичного поиска по отдельному набору возможных значений, чтобы найти расположение значения 200
для пути headquarters/employees
. Так как значения для каждого пути сортируются в возрастающем порядке, обработчику запросов достаточно выполнить двоичный поиск. После того как обработчик запросов обнаружит значение 200
, он начнет считывать все оставшиеся страницы индекса (в направлении возрастания).
Поскольку обработчик запросов может выполнять двоичный поиск, чтобы избежать сканирования ненужных страниц индекса, точное сканирование индекса, как правило, обеспечивает сравнимую задержку и затраты в RU по сравнению с операциями поиска по индексу.
Расширенная проверка индекса
Обратите внимание на следующий запрос:
SELECT *
FROM company
WHERE STARTSWITH(company.headquarters.country, "United", true)
Предикат запроса (фильтрация элементов с штаб-квартирой в местоположениях, которые начинаются словом "United" без учета регистра) можно оценить с помощью расширенного сканирования индекса по пути headquarters/country
. Операции, выполняющие расширенное сканирование индекса, имеют оптимизации, которые помогают избежать необходимости сканировать каждую страницу индекса, но они несколько дороже, чем двоичный поиск, используемый при точном сканировании индекса.
Например, при оценке StartsWith
без учета регистра механизм запросов проверяет индекс на наличие различных возможных сочетаний значений верхнего и нижнего регистра. Эта оптимизация позволяет обработчику запросов избежать чтения большинства страниц индексов. Различные системные функции имеют различные оптимизации, которые они могут использовать, чтобы избежать чтения каждой страницы индекса, поэтому они широко классифицируются как расширенная проверка индекса.
Полное сканирование индекса
Обратите внимание на следующий запрос:
SELECT *
FROM company
WHERE CONTAINS(company.headquarters.country, "United")
Предикат запроса (фильтрация по элементам, у которых штаб-квартира находится в месте с названием, содержащим "United") может быть оценен путем сканирования индекса пути headquarters/country
. В отличие от точного сканирования индекса, полная проверка индекса всегда проверяется с помощью определенного набора возможных значений, чтобы определить страницы индекса, в которых есть результаты. В этом случае CONTAINS
выполняется в индексе. Время поиска по индексу и стоимость ресурса для сканирования индексов увеличивается по мере увеличения кардинальности пути. Иными словами, чем больше различных возможных значений, которые механизму запросов необходимо сканировать, тем выше задержка и RU, при полном сканировании индекса.
Например, рассмотрим два свойства: town
и country
. Кратность города составляет 5000, а кратность country
составляет 200. Ниже приведены два примера запросов, каждый из которых имеет системную функцию CONTAINS , которая выполняет полную проверку индекса для town
свойства. Первый запрос использует больше RUs, чем второй запрос, так как кардинальность города выше, чем у country
.
SELECT *
FROM c
WHERE CONTAINS(c.town, "Red", false)
SELECT *
FROM c
WHERE CONTAINS(c.country, "States", false)
Полная проверка
В некоторых случаях обработчик запросов может не иметь возможности оценить фильтр запросов с помощью индекса. В этом случае обработчик запросов должен загрузить все элементы из хранилища транзакций, чтобы оценить фильтр запроса. Полные сканирования не используют индекс и имеют плату за использование RU, которая увеличивается линейно с общим размером данных. К счастью, операции, требующие полной проверки, являются редкими.
Векторные поисковые запросы без определенного векторного индекса
Если вы не задаёте политику индекса векторов и используете VectorDistance
системную функцию в ORDER BY
предложении, это приводит к полному сканированию и плата за единицу запроса будет выше, чем если бы вы задали политику индекса векторов. Если вы используете VectorDistance
с булевым значением, равным true, и у вас нет flat
индекса, определенного для пути вектора, происходит полное сканирование.
Запросы со сложными выражениями фильтра
В предыдущих примерах мы рассматривали только запросы с простыми выражениями фильтра (например, запросы только с одним фильтром равенства или диапазоном). В реальности большинство запросов имеют гораздо более сложные критерии фильтра.
Обратите внимание на следующий запрос:
SELECT *
FROM company
WHERE company.headquarters.employees = 200 AND CONTAINS(company.headquarters.country, "United")
Для выполнения этого запроса обработчик запросов должен выполнить поиск по индексу для headquarters/employees
и полный просмотр индекса для headquarters/country
. Обработчик запросов имеет внутренние эвристики, используемые для вычисления выражения фильтра запросов как можно быстрее. В этом случае механизму запросов не нужно считывать ненужные страницы индекса, сначала выполнив поиск по индексу. Например, только 50 элементов соответствовали фильтру равенства, обработчик запросов должен будет оценить CONTAINS
только на страницах индекса, содержащих эти 50 элементов. Полное сканирование индекса всего контейнера не потребуется.
Использование индексов для скалярных агрегатных функций
Запросы с агрегатными функциями должны зависеть только от индекса, чтобы его можно было использовать.
В некоторых случаях индекс может возвращать ложные положительные результаты. Например, при оценке CONTAINS
индекса число совпадений в индексе может превышать количество результатов запроса. Обработчик запросов загружает все совпадения индекса, вычисляет фильтр для загруженных элементов и возвращает только правильные результаты.
Для большинства запросов загрузка совпадений с ложным положительным индексом не оказывает заметного влияния на использование индекса.
Например, рассмотрим следующий запрос:
SELECT *
FROM company
WHERE CONTAINS(company.headquarters.country, "United")
Системная CONTAINS
функция может возвращать некоторые ложные положительные совпадения, поэтому обработчик запросов должен проверить, соответствует ли каждый загруженный элемент выражению фильтра. В этом примере подсистеме запросов может потребоваться только загрузка дополнительных элементов, поэтому влияние на использование индекса и плата за единицу запросов минимальна.
Однако запросы с агрегатными функциями должны зависеть только от индекса, чтобы его можно было использовать. Например, рассмотрим следующий запрос с агрегатной функцией COUNT
.
SELECT COUNT(1)
FROM company
WHERE CONTAINS(company.headquarters.country, "United")
Как и в первом примере, функция системы CONTAINS
может возвращать некоторые ложные положительные совпадения. Однако в отличие от запроса SELECT *
, запрос COUNT
не может вычислить критерий фильтра загруженных элементов для проверки всех совпадений индекса. Запрос COUNT
должен полагаться исключительно на индекс, поэтому если есть вероятность, что выражение фильтра возвращает ложные положительные совпадения, движок запросов возвращается к полному сканированию.
Запросы со следующими агрегатными функциями должны зависеть только от индекса, поэтому для оценки некоторых системных функций требуется полная проверка.