Indexing policy in Azure Cosmos DB (Политики индексации в Azure Cosmos DB)

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

В Azure Cosmos DB каждый контейнер имеет политику индексирования, определяющую, как должны индексироваться элементы контейнера. Политика индексирования по умолчанию, задаваемая для только что созданных контейнеров, индексирует каждое свойство каждого элемента и применяет диапазонные индексы для любых строк или чисел. Это позволяет получить оптимальную производительность запросов без необходимости заблаговременно заботиться об индексировании и управлении индексами.

В некоторых ситуациях полезно переопределить автоматическое поведение в соответствии с конкретными требованиями. Политику индексирования контейнера можно настроить, задавая для нее режим индексирования, включая или исключая пути к свойствам.

Примечание.

Метод обновления политик индексирования, описанных в этой статье, применяется только к API Azure Cosmos DB для NoSQL. Дополнительные сведения об индексировании приведены в статье API Azure Cosmos DB для MongoDB.

Режим индексирования

База данных Azure Cosmos DB поддерживает два режима индексирования:

  • Согласованность. Индекс обновляется синхронно при создании, обновлении или удалении элементов. Это означает, что согласованность запросов чтения будет настроена для учетной записи.
  • Нет. Индексирование в контейнере отключено. Этот режим обычно используется, если контейнер используется в качестве чистого хранилища значений ключей без необходимости в дополнительных индексах. Его также можно использовать для повышения производительности групповых операций. После выполнения групповых операций в режиме индексирования можно установить значение "Согласованность", а затем отслеживать его с помощью параметра IndexTransformationProgress до завершения.

Примечание.

Azure Cosmos DB также поддерживает режим отложенного индексирования. Отложенное индексирование выполняет обновления индекса с более низкоприоритетным уровнем, когда обработчик не выполняет никаких других действий. Это может привести к несогласованным или неполным результатам запроса. Если вы планируете запросить контейнер Azure Cosmos DB, не следует выбирать отложенное индексирование. Новые контейнеры не могут выбирать отложенное индексирование. Вы можете запросить исключение, связався cosmosdbindexing@microsoft.com (за исключением того, если вы используете учетную запись Azure Cosmos DB в бессерверном режиме, который не поддерживает отложенное индексирование).

По умолчанию политика индексирования устанавливается в значение automatic. Это осуществляется путем установки в политике индексирования для свойства automatic значения true. Задание этого свойства позволяет true Azure Cosmos DB автоматически индексировать элементы по мере записи.

Размер индексов

В базе данных Azure Cosmos DB общий использованный объем хранилища зависит от размера данных и размера индекса. Ниже приведены некоторые функции размера индекса:

  • Размер индекса зависит от политики индексирования. Если все свойства индексируются, размер индекса может быть больше, чем размер данных.
  • При удалении данных индексы сжимаются почти непрерывно. Однако при удалении данных небольшого объема вы можете не сразу заметить уменьшение размера индекса.
  • Размер индекса может временно увеличиваться при разбиении физических разделов. Индексное пространство освобождается после завершения разбиения раздела.

Включение и исключение путей к свойствам

В пользовательской политике индексации могут указываться пути к свойствам, которые явно включены в индексирование или исключены из него. Оптимизируя количество индексируемых путей, можно значительно сократить задержку и расходы ЕЗ операций записи. Эти пути определяются методом, описанным в разделе "Общие сведения об индексировании", со следующими дополнениями:

  • путь, ведущий к скалярному значению (строке или числу), заканчивается на /?
  • к элементам массива обращение происходит с помощью нотации /[] (а не /0, /1 т. д.)
  • для сопоставления с любыми элементами, расположенными ниже узла, можно использовать подстановочный знак /*

Снова выполните:

    {
        "locations": [
            { "country": "Germany", "city": "Berlin" },
            { "country": "France", "city": "Paris" }
        ],
        "headquarters": { "country": "Belgium", "employees": 250 },
        "exports": [
            { "city": "Moscow" },
            { "city": "Athens" }
        ]
    }
  • headquarters — путь employees/headquarters/employees/?

  • locations — путь country/locations/[]/country/?

  • путь к любому элементу в разделе headquarters/headquarters/*

Например, можно включить путь /headquarters/employees/?. Этот путь обеспечит индексирование свойства employees, но не будет индексировать дополнительный вложенный JSON в этом свойстве.

Стратегия включения / исключения

Любая политика индексирования должна включать корневой путь /*, который либо включается в индексирование, либо исключается из него.

  • Включите корневой путь для выборочного исключения путей, которые не нужно индексировать. Этот подход рекомендуется, так как позволяет Azure Cosmos DB заранее индексировать любое новое свойство, которое может быть добавлено в модель.

  • Включите корневой путь для выборочного включения путей, которые необходимо индексировать. Путь к свойству ключа секции по умолчанию не индексируется с помощью стратегии исключения и должен быть явно включен при необходимости.

  • Для путей с обычными символами, включающими алфавитно-цифровые символы и символ _ (подчеркивание), вам не нужно экранировать строку пути двойными кавычками (например, "/Путь/?"). Для путей с другими специальными знаками необходимо экранировать строку пути, заключенную в двойные кавычки (например, "/"path-abc"/?"). Если в строке пути предполагается наличие специальных символов, можно экранировать каждый путь для безопасности. Функционально, это не имеет никакого значения, если вы бежите каждый путь или только те, которые имеют специальные символы.

  • По умолчанию системное свойство _etag исключено из индексирования, если только etag не добавлен во включаемый путь для индексирования.

  • Если режим индексирования установлен в значение Согласованность, системные свойства id и _ts будут индексироваться автоматически.

  • Если в элементе нет явно индексированного пути, в индекс будет добавлено значение, указывающее, что путь не определен.

Все явно включенные пути будут содержать значения, добавленные в индекс для каждого элемента в контейнере, даже если путь не определен для данного элемента.

В этом разделе приведены примеры политики индексирования для включения и исключения путей.

Приоритет включения / исключения

Если включаемые и исключаемые пути конфликтуют, приоритет будет имеет более точный путь.

Приведем пример:

Включаемый путь: /food/ingredients/nutrition/*

Исключаемый путь: /food/ingredients/*

В этом случае включенный путь имеет приоритет над исключенным путем, так как это более точно. На основе этих путей все данные в пути food/ingredients или вложенные в них пути не будут индексироваться. Исключением могут быть данные внутри включаемого пути: /food/ingredients/nutrition/*, который будет индексироваться.

Ниже приведены некоторые правила для приоритета включаемых и исключаемых путей в Azure Cosmos DB.

  • Более глубокие пути более точны, чем неглубокие пути. Например: /a/b/? более точен, чем /a/?.

  • /? более точен, чем /*. Например, /a/? более точен, чем /a/*, поэтому /a/? имеет больший приоритет.

  • Путь /* должен содержать либо включаемый путь, либо исключаемый путь.

Пространственные индексы

При определении пространственного пути в политике индексации необходимо определить, какой индекс type следует применить к этому пути. К пространственным индексам могут относиться следующие типы:

  • Point

  • Многоугольник

  • MultiPolygon

  • LineString

Azure Cosmos DB по умолчанию не создает пространственные индексы. Если необходимо использовать встроенные функции пространственного SQL, необходимо создать пространственный индекс для обязательных свойств. В этом разделе приведены примеры политики индексации для добавления пространственных индексов.

Составные индексы

Для запросов, имеющих предложение ORDER BY с двумя или более свойствами, требуется составной индекс. Кроме того, можно определить составной индекс, чтобы повысить производительность многих запросов на равенство и на диапазоны. По умолчанию составные индексы не определяются, поэтому при необходимости следует добавлять составные индексы.

В отличие от включаемых или исключаемых путей, невозможно создать путь с подстановочным знаком /*. Каждый составной путь имеет неявный элемент /? в конце пути, который не нужно указывать. Составные пути приводят к скалярным значениям, которое является единственным значением, включенным в составной индекс. Если путь в составном индексе не существует в элементе или приводит к не скалярным значениям, в индекс будет добавлено значение, указывающее, что путь не определен.

При определении составного индекса необходимо указать:

  • Два или более пути свойств. Последовательность, в которой определяются пути к свойствам, имеет значение.

  • Сортировка (по возрастанию или по убыванию).

Примечание.

При добавлении составного индекса запрос будет использовать существующие индексы диапазона до завершения добавления нового составного индекса. Таким образом, при добавлении составного индекса вы можете не сразу заметить улучшение производительности. Ход преобразования индекса можно отслеживать с помощью одного из пакетов SDK.

Запросы ORDER BY (сортировка) по нескольким свойствам:

Следующие рекомендации применяются при использовании составных индексов для запросов с предложением ORDER BY по двум или более свойствам:

  • Если пути составного индекса не соответствуют последовательности свойств в ORDER BY предложении, составной индекс не может поддерживать запрос.

  • Сортировка путей составных индексов (по возрастанию или по убыванию) должна также совпадать с параметром order в предложении ORDER BY.

  • Составной индекс также поддерживает предложение ORDER BY с противоположной сортировкой для всех путей.

Рассмотрим следующий пример, в котором составной индекс определяется для свойств Name, Age и _ts.

Составной индекс Пример запросаORDER BY Поддерживается составным индексом?
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name ASC, c.age asc Yes
(name ASC, age ASC) SELECT * FROM c ORDER BY c.age ASC, c.name asc No
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name DESC, c.age DESC Yes
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name ASC, c.age DESC No
(name ASC, age ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.age ASC, timestamp ASC Yes
(name ASC, age ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.age ASC No

Необходимо настроить политику индексирования для обслуживания всех необходимых запросов ORDER BY.

запросы с фильтрами по нескольким свойствам.

Если запрос имеет фильтры по двум или более свойствам, может оказаться полезным, чтобы создать составной индекс для этих свойств.

Например, рассмотрим следующий запрос, имеющий фильтр равенства и диапазона:

SELECT *
FROM c
WHERE c.name = "John" AND c.age > 18

Этот запрос будет более эффективным, что занимает меньше времени и потребляет меньше единиц запросов, если он может использовать составной индекс (name ASC, age ASC).

Запросы с несколькими фильтрами по диапазону также можно оптимизировать с помощью составного индекса. Однако каждый отдельный составной индекс может оптимизировать только один фильтр по диапазону. Фильтры диапазонов включают >, <, <=, >= и !=. Фильтр по диапазону должен определяться последним в составном индексе.

Рассмотрим следующий запрос с фильтром по равенству и двумя фильтрами по диапазону:

SELECT *
FROM c
WHERE c.name = "John" AND c.age > 18 AND c._ts > 1612212188

Этот запрос будет более эффективным с составным индексом в (name ASC, age ASC) и (name ASC, _ts ASC). Однако запрос не будет использовать составной индекс, (age ASC, name ASC) так как свойства с фильтрами равенства должны быть определены сначала в составном индексе. В (name ASC, age ASC, _ts ASC) вместо одного составного индекса должны использоваться два отдельных составных индекса, так как каждый составной индекс может оптимизировать только один фильтр по диапазону.

При создании составных индексов для запросов с фильтрами по нескольким свойствам используются следующие рекомендации

  • В выражениях фильтра могут использоваться несколько составных индексов.
  • Свойства в фильтре запроса должны соответствовать свойствам в составном индексе. Если свойство находится в составном индексе, но не входит в запрос в качестве фильтра, запрос не будет использовать составной индекс.
  • Если запрос имеет другие свойства в фильтре, который не определен в составном индексе, то для оценки запроса будет использоваться сочетание составных и диапазонных индексов. Для этого потребуется меньше единиц запросов, чем исключительно с использованием индексов диапазона.
  • Если свойство имеет фильтр по диапазону (>, <, <=, >= или !=), то это свойство должно определяться последним в составном индексе. Если запрос содержит более одного фильтра по диапазону, может оказаться полезным использовать несколько составных индексов.
  • При создании составного индекса для оптимизации запросов с несколькими фильтрами ORDER составного индекса не окажет влияния на результаты. Это необязательное свойство.

Рассмотрим следующий пример, в котором составной индекс определяется по свойствам Name, Age и timestamp.

Составной индекс Пример запроса Поддерживается составным индексом?
(name ASC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age = 18 Yes
(name ASC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name ASC, age ASC) SELECT COUNT(1) FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name DESC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name ASC, age ASC) SELECT * FROM c WHERE c.name != "John" AND c.age > 18 No
(name ASC, age ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 123049923 Yes
(name ASC, age ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age < 18 AND c.timestamp = 123049923 No
(name ASC, age ASC) and (name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age < 18 AND c.timestamp > 123049923 Yes

Запросы с фильтром и ORDER BY

Если в запросе фильтруется одно или несколько свойств и имеются различные свойства в предложении ORDER BY, можно добавить свойства в фильтр в предложении ORDER BY.

Например, добавив свойства в фильтр в предложение ORDER BY, можно переписывать следующий запрос, чтобы использовать составной индекс:

Запрос с использованием индекса по диапазону:

SELECT *
FROM c 
WHERE c.name = "John" 
ORDER BY c.timestamp

Запрос с использованием составного индекса:

SELECT * 
FROM c 
WHERE c.name = "John"
ORDER BY c.name, c.timestamp

Одни и те же оптимизации запросов могут быть обобщены для запросов ORDER BY с фильтрами, с учетом того, что отдельные составные индексы могут поддерживать только один фильтр по диапазону.

Запрос с использованием индекса по диапазону:

SELECT * 
FROM c 
WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 1611947901 
ORDER BY c.timestamp

Запрос с использованием составного индекса:

SELECT * 
FROM c 
WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 1611947901 
ORDER BY c.name, c.age, c.timestamp

Кроме того, можно использовать составные индексы для оптимизации запросов с помощью системных функций и директивы ORDER BY.

Запрос с использованием индекса по диапазону:

SELECT * 
FROM c 
WHERE c.firstName = "John" AND Contains(c.lastName, "Smith", true) 
ORDER BY c.lastName

Запрос с использованием составного индекса:

SELECT * 
FROM c 
WHERE c.firstName = "John" AND Contains(c.lastName, "Smith", true) 
ORDER BY c.firstName, c.lastName

При создании составных индексов для оптимизации запроса с помощью фильтра и предложения ORDER BY следует учитывать следующие моменты.

  • Если вы не определяете составной индекс для запроса с фильтром по одному свойству и отдельному ORDER BY предложению с использованием другого свойства, запрос по-прежнему будет выполнен успешно. Однако стоимость единицы запроса может быть сокращена с помощью составного индекса, особенно если свойство в предложении ORDER BY имеет высокую кратность.
  • Если запрос фильтрует свойства, эти свойства должны быть включены сначала в ORDER BY предложение.
  • Если в запросе используется фильтр по нескольким свойствам, фильтры по равенству должны быть первыми свойствами в предложении ORDER BY.
  • Если в запросе используется фильтр по нескольким свойствам, то для одного составного индекса можно использовать не более одного фильтра по диапазону или системной функции. Свойство, используемое в фильтре по диапазону или системной функции, должно определяться в составном индексе последним.
  • Все рекомендации, относящиеся к созданию составных индексов для запросов ORDER BY с несколькими свойствами, а также запросов с фильтрами по нескольким свойствам, по-прежнему применяются.
Составной индекс Пример запросаORDER BY Поддерживается составным индексом?
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.name ASC, c.timestamp ASC Yes
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.timestamp > 1589840355 ORDER BY c.name ASC, c.timestamp ASC Yes
(timestamp ASC, name ASC) SELECT * FROM c WHERE c.timestamp > 1589840355 AND c.name = "John" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.timestamp ASC No
(age ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.age = 18 and c.name = "John" ORDER BY c.age ASC, c.name ASC,c.timestamp ASC Yes
(age ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.age = 18 and c.name = "John" ORDER BY c.timestamp ASC No

Запросы с фильтром и статистическим выражением

Если в запросе используется фильтр по одному или нескольким свойствам, а также системная функция статистического выражения, можно создать составной индекс для свойств в фильтре системной функции статистического выражения. Эта оптимизация применяется к системным функциям Sum и AVG.

При создании составных индексов для оптимизации запроса с помощью фильтра и системной функции статистического выражения.

  • Составные индексы необязательны при выполнении запросов со статистическими выражениями. Однако стоимость единицы запроса может значительно снизиться при использовании составного индекса.
  • Если в запросе используется фильтр по нескольким свойствам, фильтры по равенству должны быть первыми свойствами составном индексе.
  • Можно использовать не более одного фильтра по диапазону для каждого составного индекса, и фильтр должен находиться в свойстве в системной функции статистического выражения.
  • Свойство в системной функции статистического выражения должно определяться последним в составном индексе.
  • Значение order (ASC или DESC) не имеет значения.
Составной индекс Пример запроса Поддерживается составным индексом?
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" Yes
(timestamp ASC, name ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" No
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name > "John" No
(name ASC, age ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" AND c.age = 25 Yes
(age ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" AND c.age > 25 No

Изменение политики индексирования

Политику индексирования контейнера можно обновить в любое время с помощью портала Azure или одного из поддерживаемых пакетов SDK. Обновление политики индексирования запускает процесс преобразования старого индекса в новый, этот процесс выполняется в сети и на месте (поэтому дополнительное пространство для хранения данных во время операции не расходуется). Предыдущая политика индексирования эффективно преобразуется в новую политику без влияния на запись и чтение или на подготовленную для контейнера пропускную способность. Преобразование индекса — это асинхронная операция, а время, необходимое для ее выполнения, зависит от подготовленной пропускной способности, количества элементов и их размера. Если необходимо выполнить несколько обновлений политики индексирования, рекомендуется выполнить все изменения в виде одной операции, чтобы преобразование индекса было завершено как можно быстрее.

Важно!

Преобразование индекса — это операция, использующая единицы запросов. Единицы запросов, расходуемые в процессе преобразования индекса, в настоящее время не учитываются при использовании бессерверных контейнеров. Счета за эти единицы запроса будут выставляться после того, как бессерверные контейнеры станут доступными.

Примечание.

Вы можете отслеживать ход преобразования индекса в портал Azure или с помощью одного из пакетов SDK.

Во время преобразований индекса не влияет на доступность записи. В преобразовании индекса используется подготовленные ЕЗ, но с более низким приоритетом, чем операции CRUD или запросы.

При добавлении новых индексированных путей не влияет на доступность чтения. Запросы будут использовать новые проиндексированные пути только по завершении преобразования индекса. Другими словами, при добавлении нового индексированного пути запросы, которые получают преимущество от индексированного пути, будут иметь одинаковую производительность до и во время преобразования индекса. После завершения преобразования индекса обработчик запросов начнет использовать новые проиндексированные пути.

При удалении проиндексированных путей необходимо сгруппировать все изменения в одно преобразование политики индексирования. Если удалить несколько индексов и сделать это в одном изменении одной политики индексирования, обработчик запросов обеспечит согласованность и целостность результатов в рамках преобразования индекса. Однако при удалении индексов с помощью нескольких изменений политики индексирования подсистема запросов не будет предоставлять согласованные или полные результаты до завершения всех преобразований индекса. Большинство разработчиков не сбрасывают индексы, а затем немедленно пытаются выполнять запросы, использующие эти индексы, так что на практике это маловероятно.

При удалении индексированного пути подсистема запросов немедленно перестанет использовать его и выполнит полную проверку.

Примечание.

По возможности следует всегда пытаться сгруппировать несколько удалений индексов в одну изменение политики индексирования.

Важно!

Удаление индекса влияет немедленно, в то время как добавление нового индекса занимает некоторое время, так как требуется преобразование индексирования. При замене одного индекса другим (например, заменяя один индекс свойств составным индексом) сначала добавьте новый индекс, а затем дождитесь завершения преобразования индекса перед удалением предыдущего индекса из политики индексирования. В противном случае это негативно повлияет на способность запрашивать предыдущий индекс и может нарушить активные рабочие нагрузки, ссылающиеся на предыдущий индекс.

Политики индексирования и TTL

При использовании функции срока жизни (TTL) требуется индексирование. Это означает следующее.

  • Невозможно активировать TTL в контейнере, в котором задан noneрежим индексирования .
  • Невозможно задать для режима индексирования значение None в контейнере, где активируется TTL.

В сценариях, где не требуется индексировать путь свойства, но требуется TTL, можно использовать политику индексирования с режимом, установленным в значение consistent, без включенных путей, и /* только с исключенными путями.

Следующие шаги

Дополнительные сведения об индексировании см. по следующим ссылкам: