Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Cosmos DB (в Azure и Fabric) — это не зависящая от схемы база данных, которая позволяет выполнять итерацию в приложении без необходимости выполнять управление схемами или индексами. Эта функциональность также называется схемой при чтении, что означает, что Cosmos DB не применяет схему к вашим данным при записи их в базу данных. Схема определена в классах, создаваемых в приложении, при десериализации данных из базы данных во время операций чтения или записи.
Индексирование в Cosmos DB в Microsoft Fabric предназначено для обеспечения быстрой и гибкой производительности запросов независимо от того, как развиваются данные. По умолчанию Cosmos DB автоматически индексирует каждое свойство для всех элементов в контейнере без необходимости определять схему или настраивать вторичные индексы.
Концептуальное дерево
Каждый раз, когда элемент сохраняется в контейнере, его содержимое проецируется как документ JSON, а затем преобразуется в представление в виде дерева. Это преобразование означает, что каждое свойство этого элемента представляется как узел в дереве. Псевдокорневой узел создается как родитель для всех свойств элемента первого уровня. Листовые узлы содержат фактические скалярные значения, перенесенные элементом.
Рассмотрим этот элемент в качестве примера:
{
"id": "00000000-0000-0000-0000-000000004368",
"name": "Cofaz Jacket",
"tags": [
{ "category": "clothing", "type": "jacket" },
{ "category": "outdoor", "type": "winter" }
],
"inventory": { "warehouse": "Seattle", "quantity": 50 },
"distributors": [
{ "name": "Contoso" },
{ "name": "AdventureWorks" }
]
}
Это концептуальное дерево представляет пример элемента JSON:
-
id:00000000-0000-0000-0000-000000004368 -
name:Cofaz Jacket tags0-
category:clothing -
type:jacket
-
1-
category:outdoor -
type:winter
-
inventory-
warehouse:Seattle -
quantity:50
-
distributors0-
name:Contoso
-
1-
name:AdventureWorks
-
Обратите внимание на то, как массивы кодируются в дереве: каждая запись в массиве получает промежуточный узел, помеченный индексом этой записи в массиве. Например, первая запись — это 0, и вторая запись — 1.
Пути к свойствам
Cosmos DB преобразует элементы в деревья, так как позволяет системе ссылаться на свойства с помощью путей в этих деревьях. Чтобы получить путь к свойству, мы можем перемещаться по дереву с корневого узла к этому свойстве и объединять метки каждого пройденного узла.
Ниже приведены пути для каждого свойства из примера элемента, описанного ранее:
| Путь | Ценность |
|---|---|
/id |
"00000000-0000-0000-0000-000000004368" |
/name |
"Cofaz Jacket" |
/tags/0/category |
"clothing" |
/tags/0/type |
"jacket" |
/tags/1/category |
"outdoor" |
/tags/1/type |
"winter" |
/inventory/warehouse |
"Seattle" |
/inventory/quantity |
50 |
/distributors/0/name |
"Contoso" |
/distributors/1/name |
"AdventureWorks" |
Cosmos DB эффективно индексирует путь каждого свойства и соответствующее значение при записи элемента.
Типы индексов в Cosmos DB
Cosmos DB в настоящее время поддерживает четыре типа индексов.
Такие типы индексов можно настроить при определении политики индексации.
Индекс диапазона
Индексы диапазона основаны на упорядоченной структуре дерева. Индексы диапазонов используются в следующих случаях:
Запросы о равенстве
SELECT * FROM container c WHERE c.property = 'value'SELECT * FROM container c WHERE c.property IN ("value1", "value2", "value3")совпадение для элемента массива
SELECT * FROM container c WHERE ARRAY_CONTAINS(c.tags, "tag1")Запросы к диапазону:
SELECT * FROM container c WHERE c.property > 0Замечание
Работает для
>,<,>=,<=,!=Проверка наличия свойства:
SELECT * FROM container c WHERE IS_DEFINED(c.property)Строковые системные функции:
SELECT * FROM container c WHERE CONTAINS(c.property, "value")SELECT * FROM container c WHERE STRINGEQUALS(c.property, "value")Запросы
ORDER BY:SELECT * FROM container c ORDER BY c.propertyЗапросы
JOIN:SELECT d FROM container c JOIN d IN c.properties WHERE d = 'value'
Индексы диапазона можно использовать для скалярных значений (строка или число). Политика индексирования по умолчанию, задаваемая для только что созданных контейнеров, применяет диапазонные индексы для любых строк или чисел.
Замечание
Предложение ORDER BY, которое всегда упорядочивает по одному свойству, нуждается в индексе диапазона и завершается ошибкой, если путь, на который оно ссылается, не имеет индекса диапазона. Аналогичным образом запрос ORDER BY, который упорядочивает по нескольким свойствам, всегда нуждается в составном индексе.
Пространственный индекс
Пространственные индексы обеспечивают эффективные запросы для геопространственных объектов, таких как точки, линии, полигоны и многополигионы. Эти запросы используют , ST_DISTANCEST_WITHINST_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 container c WHERE ST_INTERSECTS(c.property, { 'type':'Polygon', 'coordinates': [[ [31.8, -5], [32, -5], [31.8, -5] ]] })
Пространственные индексы можно использовать для правильно отформатированных объектов GeoJSON. В настоящее время поддерживаются Points, LineStrings, Polygons и MultiPolygons.
Составной индекс
Составные индексы повышают эффективность при выполнении операций с несколькими полями. Составные индексы используются в таких случаях:
Запросы
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.department = "Information Technology" and CONTAINS(c.team, "Pilot"):
Этот запрос сначала применяет фильтр для записей, в которых
department = "Information Technology"используется индекс. Затем он передает всеdepartment = "Information Technology"записи через последующий конвейер для оценкиCONTAINSпредиката фильтра.Вы можете ускорить запросы и избежать полного сканирования контейнеров при использовании функций, выполняющих полную проверку, например
CONTAINS. Вы можете добавить дополнительные предикаты фильтров, которые используют индекс для ускорения этих запросов. Порядок условий фильтра не имеет значения. Модуль запросов определяет, какие предикаты являются более выборочными и выполняют запрос соответствующим образом.
Векторный индекс
Индексы векторов повышают эффективность при выполнении векторного поиска с помощью системной VECTORDISTANCE функции. Поиск вектора имеет более низкую задержку, более высокую пропускную способность и меньшее потребление единиц запросов (RU) при использовании векторного индекса. Cosmos DB поддерживает любые векторные внедрения (текст, изображение, многомодальные и т. д.) размером до 4096 размеров.
ORDER BYзапросы векторного поиска:SELECT TOP 10 * FROM container c ORDER BY VECTORDISTANCE(c.vector1, c.vector2)Проекция оценки сходства в векторных поисковых запросах:
SELECT TOP 10 c.name, VECTORDISTANCE(c.vector1, c.vector2) AS score FROM container c ORDER BY VECTORDISTANCE(c.vector1, c.vector2)Диапазонные фильтры по оценке сходства.
SELECT TOP 10 * FROM container c WHERE VECTORDISTANCE(c.vector1, c.vector2) > 0.8 ORDER BY VECTORDISTANCE(c.vector1, c.vector2)
Замечание
Как правило, можно добавить новые конфигурации пути или удалить существующие, но нельзя изменить параметры политики встраивания вектора или индексной политики векторов. Для этого необходимо сначала удалить существующую политику вектора и индекс, а затем добавить ее обратно с новой конфигурацией.
Как запросы используют индексы
Существует пять способов, с помощью которых механизм запросов может оценивать фильтры запросов, отсортированные от наиболее эффективных до наименее эффективных.
- Поиск в индексе
- Точная проверка индексов
- Расширенная проверка индекса
- Полное сканирование индекса
- Полная проверка
При индексировании путей свойств обработчик запросов автоматически использует индекс как можно эффективнее. Помимо индексирования новых путей свойств, для оптимизации того, как запросы используют индекс, не нужно настраивать что-либо еще. RU-начисление за запрос является сочетанием RU-начисления за использование индекса и RU-начисления при загрузке элементов.
В следующей таблице приведены различные способы использования индексов в Cosmos DB:
| Тип поиска | Description | Распространенные примеры | Плата за использование индекса | Плата за загрузку элементов из хранилища транзакций |
|---|---|---|---|---|
| Поиск в индексе | Чтение только необходимых индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | Фильтры равенства, IN | Константа для фильтра равенства | Увеличивается в зависимости от количества элементов в результатах запроса |
| Точное сканирование индекса | Двоичный поиск индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | Сравнения диапазонов (>, <, <=, или >=), StartsWith | Сравнимо с поиском по индексу, увеличение слегка зависит от кардинальности индексируемых свойств. | Увеличивается в зависимости от количества элементов в результатах запроса |
| Расширенная проверка индекса | Оптимизированный поиск (но менее эффективный, чем двоичный поиск) индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | StartsWith (без учета регистра), StringEquals (без учета регистра) | Незначительно увеличивается в зависимости от кардинальности индексированных свойств | Увеличивается в зависимости от количества элементов в результатах запроса |
| Полная проверка индекса | Чтение конкретного набора индексированных значений и загрузка только совпадающих элементов из транзакционного хранилища данных | Содержит, EndsWith, RegexMatch, LIKE | Увеличивается линейно в зависимости от количества элементов индексированных свойств | Увеличивается в зависимости от количества элементов в результатах запроса |
| Полный просмотр | Загрузка всех элементов из хранилища транзакционных данных | Верхний, Нижний | N/A | Увеличивается в зависимости от числа элементов в контейнере |
При написании запросов следует использовать предикаты фильтров, которые используют индекс максимально эффективно. Например, если для вашего варианта использования подойдет либо StartsWith, либо Contains, вам следует выбрать StartsWith, так как он выполняет точное сканирование индекса вместо полного сканирования индекса.
Данные об использовании индекса
Подсказка
В этом разделе рассматриваются дополнительные сведения о том, как запросы используют индексы. Этот уровень детализации не требуется, чтобы узнать, как приступить к работе с Cosmos DB, но подробно описан для любопытных пользователей. Мы ссылаемся на пример элемента, доступного ранее в этом документе:
Рассмотрим следующие два примера элементов:
[
{
"id": "00000000-0000-0000-0000-000000004368",
"name": "Cofaz Jacket",
"tags": [
{ "category": "clothing", "type": "jacket" },
{ "category": "outdoor", "type": "winter" }
],
"inventory": { "warehouse": "Seattle", "quantity": 50 },
"distributors": [
{ "name": "Contoso" },
{ "name": "AdventureWorks" }
]
},
{
"id": "00000000-0000-0000-0000-000000004002",
"name": "Potana bike",
"tags": [
{ "category": "cycling", "type": "mountain" }
],
"inventory": { "warehouse": "Seattle", "quantity": 30 },
"distributors": [
{ "name": "Contoso" },
{ "name": "Fabrikam" },
{ "name": "Northwind" }
]
}
]
Cosmos DB использует инвертированные индексы. Индекс работает путем сопоставления каждого пути JSON с набором элементов, содержащих это значение. Сопоставление идентификаторов элементов представлено на множестве разных страниц индекса для контейнера. Ниже приведен пример схемы инвертированного индекса для контейнера, включающего два примера элементов:
| Путь | Ценность | Список идентификаторов элементов |
|---|---|---|
/tags/0/category |
clothing |
[00000000-0000-0000-0000-000000004368] |
/tags/0/category |
cycling |
[00000000-0000-0000-0000-000000004002] |
/tags/0/type |
jacket |
[00000000-0000-0000-0000-000000004368] |
/tags/0/type |
mountain |
[00000000-0000-0000-0000-000000004002] |
/tags/1/category |
outdoor |
[00000000-0000-0000-0000-000000004368] |
/tags/1/type |
winter |
[00000000-0000-0000-0000-000000004368] |
/inventory/warehouse |
Seattle |
[00000000-0000-0000-0000-000000004368, 00000000-0000-0000-0000-000000004002] |
/inventory/quantity |
30 |
[00000000-0000-0000-0000-000000004002] |
/inventory/quantity |
50 |
[00000000-0000-0000-0000-0000-000000004001] |
Инвертированный индекс имеет два важных атрибута.
Для данного пути значения сортируются в возрастающем порядке. Таким образом, обработчик запросов может легко извлекать
ORDER BYиз индекса.Для заданного пути поисковый движок может сканировать через различный набор возможных значений, чтобы определить страницы индекса, на которых имеются результаты.
Обработчик запросов может использовать инвертированный индекс четырьмя разными способами.
Поиск в индексе
Обратите внимание на следующий запрос:
SELECT
tag
FROM
tag IN product.tags
WHERE
tag.category = 'outdoor'
Предикат запроса (фильтрация по элементам, где любой из тегов имеет "на открытом воздухе" в качестве категории) будет соответствовать пути, специально указанному здесь:
tags1-
category:outdoor
-
Поскольку этот запрос имеет фильтр равенства, после обхода этого дерева можно быстро определить страницы индекса, содержащие результаты запроса. В этом случае обработчик запросов считывает страницы индекса, содержащие элемент 00000000-0000-0000-0000-000000004368. Поиск по индексу является наиболее эффективным способом использования индекса. При поиске индекса мы считываем только необходимые страницы индекса и загружаем только элементы в результатах запроса. Таким образом, время поиска по индексу и расходы в RU за поиск по индексу чрезвычайно низкие, независимо от общего объема данных.
Точная проверка индексов
Обратите внимание на следующий запрос:
SELECT
*
FROM
product
WHERE
product.inventory.quantity > 30
Предикат запроса (фильтрация элементов, в которых запас превышает 30 единиц) можно оценить с помощью точного сканирования индекса для пути inventory/quantity. При выполнении точной проверки индекса обработчик запросов начинает с двоичного поиска по отдельному набору возможных значений, чтобы найти расположение значения 30 для пути inventory/quantity. Так как значения для каждого пути сортируются в возрастающем порядке, обработчику запросов достаточно выполнить двоичный поиск. После того как обработчик запросов обнаружит значение 30, он начнет считывать все оставшиеся страницы индекса (в направлении возрастания).
Поскольку обработчик запросов может выполнять двоичный поиск, чтобы избежать сканирования ненужных страниц индекса, точное сканирование индекса, как правило, обеспечивает сравнимую задержку и затраты в RU по сравнению с операциями поиска по индексу.
Расширенная проверка индекса
Обратите внимание на следующий запрос:
SELECT
*
FROM
product
WHERE
STARTSWITH(product.inventory.warehouse, "Sea", true)
Предикат запроса (фильтрация по элементам, имеющим запас на складе, который начинается с "Sea" без учета регистра) может быть оценен с помощью расширенного сканирования индекса пути inventory/warehouse. Операции, выполняющие расширенное сканирование индекса, имеют оптимизации, которые помогают избежать необходимости сканировать каждую страницу индекса, но они несколько дороже, чем двоичный поиск, используемый при точном сканировании индекса.
Например, при оценке StartsWith без учета регистра механизм запросов проверяет индекс на наличие различных возможных сочетаний значений верхнего и нижнего регистра. Эта оптимизация позволяет обработчику запросов избежать чтения большинства страниц индексов. Различные системные функции имеют различные оптимизации, которые они могут использовать, чтобы избежать чтения каждой страницы индекса, поэтому они широко классифицируются как расширенная проверка индекса.
Полное сканирование индекса
Обратите внимание на следующий запрос:
SELECT
*
FROM
product
WHERE
CONTAINS(product.inventory.warehouse, "eat")
Предикат запроса (фильтрация по элементам, имеющим инвентаризацию в складе, который содержит 'eat') можно использовать с помощью сканирования пути индекса inventory/warehouse. В отличие от точного сканирования индекса, полная проверка индекса всегда проверяется с помощью определенного набора возможных значений, чтобы определить страницы индекса, в которых есть результаты. В этом случае CONTAINS выполняется в индексе. Время поиска по индексу и стоимость ресурса для сканирования индексов увеличивается по мере увеличения кардинальности пути. Иными словами, чем больше различных возможных значений, которые механизму запросов необходимо сканировать, тем выше задержка и RU, при полном сканировании индекса.
Например, рассмотрим два свойства: name и warehouse. Кратность имени составляет 5000, а кратность warehouse — 200. Ниже приведены два примера запросов, каждая из которых имеет системную CONTAINS функцию, которая выполняет полную проверку индекса для name свойства. Первый запрос использует больше запросных единиц (РЕ), чем второй запрос, потому что кардинальность имени выше warehouse.
SELECT
*
FROM
container c
WHERE
CONTAINS(c.name, "Pack", false)
SELECT
*
FROM
c
WHERE
CONTAINS(c.inventory.warehouse, "Sea", false)
Полная проверка
В некоторых случаях обработчик запросов может не иметь возможности оценить фильтр запросов с помощью индекса. В этом случае обработчик запросов должен загрузить все элементы из хранилища транзакций, чтобы оценить фильтр запроса. Полные сканирования не используют индекс и имеют плату за использование RU, которая увеличивается линейно с общим размером данных. К счастью, операции, требующие полной проверки, являются редкими.
Векторные поисковые запросы без определенного векторного индекса
Если вы не зададите политику векторного индекса и используете VECTORDISTANCE системную функцию в ORDER BY предложении, этот запрос приведет к полному сканированию, и будет взиматься плата за RUs выше, чем если бы вы установили политику векторного индекса. Если вы используете VECTORDISTANCE с булевым значением, равным true, и у вас нет flat индекса, определенного для пути вектора, происходит полное сканирование.
Запросы со сложными выражениями фильтра
В предыдущих примерах мы рассматривали только запросы с простыми выражениями фильтра (например, запросы только с одним фильтром равенства или диапазоном). В реальности большинство запросов имеют гораздо более сложные критерии фильтра.
Обратите внимание на следующий запрос:
SELECT
*
FROM
product
WHERE
product.inventory.quantity = 50 AND CONTAINS(product.inventory.warehouse, "Sea")
Для выполнения этого запроса обработчик запросов должен выполнить поиск по индексу для inventory/quantity и полный просмотр индекса для inventory/warehouse. Обработчик запросов имеет внутренние эвристики, используемые для вычисления выражения фильтра запросов как можно быстрее. В этом случае механизму запросов не нужно считывать ненужные страницы индекса, сначала выполнив поиск по индексу. Например, только 50 элементов соответствовали фильтру равенства, обработчик запросов должен будет оценить CONTAINS только на страницах индекса, содержащих эти 50 элементов. Полное сканирование индекса всего контейнера не потребуется.
Использование индексов для скалярных агрегатных функций
Запросы с агрегатными функциями должны зависеть только от индекса, чтобы его можно было использовать.
В некоторых случаях индекс может возвращать ложные положительные результаты. Например, при оценке CONTAINS индекса число совпадений в индексе может превышать количество результатов запроса. Обработчик запросов загружает все совпадения индекса, вычисляет фильтр для загруженных элементов и возвращает только правильные результаты.
Для большинства запросов загрузка совпадений с ложным положительным индексом не оказывает заметного влияния на использование индекса.
Например, рассмотрим следующий запрос:
SELECT
*
FROM
product
WHERE
CONTAINS(product.inventory.warehouse, "Sea")
Системная CONTAINS функция может возвращать некоторые ложные положительные совпадения, поэтому обработчик запросов должен проверить, соответствует ли каждый загруженный элемент выражению фильтра. В этом примере подсистеме запросов может потребоваться только загрузка дополнительных элементов, поэтому влияние на использование индекса и плата за единицу запросов минимальна.
Однако запросы с агрегатными функциями должны зависеть только от индекса, чтобы его можно было использовать. Например, рассмотрим следующий запрос с агрегатной функцией COUNT.
SELECT
COUNT(1)
FROM
product
WHERE
CONTAINS(product.inventory.warehouse, "Sea")
Как и в первом примере, функция системы CONTAINS может возвращать некоторые ложные положительные совпадения. Однако в отличие от запроса SELECT *, запрос COUNT не может вычислить критерий фильтра загруженных элементов для проверки всех совпадений индекса. Запрос COUNT должен полагаться исключительно на индекс, поэтому если есть вероятность, что выражение фильтра возвращает ложные положительные совпадения, движок запросов возвращается к полному сканированию.
Запросы со следующими агрегатными функциями должны зависеть только от индекса, поэтому для оценки некоторых системных функций требуется полная проверка.
Связанный контент
- политики индексации в Cosmos DB (в Azure и Fabric)
- Примеры политик индексирования в Cosmos DB (в Azure и Fabric)