Иерархические ключи секций в Azure Cosmos DB

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

Azure Cosmos DB распределяет данные между логическими и физическими секциями на основе ключей секций для поддержки горизонтального масштабирования. Используя иерархические ключи секций (также называемые подпартитонингом), можно настроить до трехуровневой иерархии для ключей секций для дальнейшего оптимизации распределения данных и повышения уровня масштабирования.

Если вы используете искусственные ключи сегодня, у вас есть сценарии, в которых ключи секций могут превышать 20 ГБ данных, или хотите убедиться, что документ каждого клиента сопоставляется с собственной логической секцией, подсекция может помочь. При использовании этой функции префиксы ключа логического раздела могут превышать 20 ГБ и 10 000 единиц запросов в секунду (ЕЗ/с). Запросы по префиксу эффективно направляются в подмножество секций, в которых хранятся данные.

Выбор иерархических ключей секций

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

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

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

  • Для всех контейнеров каждый уровень полного пути (начиная с первого уровня) ключа иерархической секции должен:

    • Имеет высокую кратность. Первый, второй и третий (если применимо) ключи иерархической секции должны иметь широкий диапазон возможных значений.

      • Наличие низкой кратности на первом уровне иерархического ключа секции ограничивает все операции записи во время приема до одной физической секции, пока не достигнет 50 ГБ и разбивается на две физические секции. Например, предположим, что ключ первого уровня включен TenantId и имеет только 5 уникальных клиентов. Каждая из этих операций клиентов будет ограничена только одной физической секцией, что ограничивает потребление пропускной способности только тем, что находится в этой физической секции. Это связано с тем, что иерархические секции оптимизируют для всех документов с одним и тем же ключом первого уровня для сортировки в одной физической секции, чтобы избежать полнофункционального запроса.
      • Хотя это может быть хорошо для рабочих нагрузок, где мы делаем однократный прием всех данных наших клиентов, и следующие операции в основном удобочитаются после этого, это может быть бездействие для рабочих нагрузок, где ваши бизнес-требования включают прием данных в течение определенного времени. Например, если у вас есть строгие бизнес-требования, чтобы избежать задержек, максимальная пропускная способность рабочей нагрузки теоретически может достичь приема данных — это количество физических секций * 10k. Если ключ верхнего уровня имеет низкую кратность, число физических секций, скорее всего, будет 1, если нет достаточных данных для ключа уровня 1 для его распространения по нескольким секциям после разделения, которое может занять от 4 до 6 часов.
    • Равномерное использование единиц запросов и хранилище данных по всем логическим секциям. Это обеспечивает равномерное потребление единиц запроса и распределение объемов хранилища по физическим секциям.

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

    • Например, рабочая нагрузка, которая часто выполняет запросы для фильтрации определенных сеансов пользователей в мультитенантном приложении, может воспользоваться иерархическими ключами секций TenantIdи UserIdSessionIdв этом порядке. Запросы можно эффективно перенаправлять в нужные физические секции, включив ключ секции в предикат фильтра. Дополнительные сведения о выборе ключей секций для рабочих нагрузок с большим объемом чтения см. в обзоре секционирования.
  • Рабочие нагрузки с высокой нагрузкой на запись: рекомендуется использовать большое значение для первого уровня иерархического ключа секции. Высокая кратность означает, что ключ первого уровня (и последующие уровни) имеет по крайней мере тысячи уникальных значений и более уникальных значений, чем число физических секций.

    • Например, предположим, что у нас есть рабочая нагрузка, которая изолирует клиентов по ключу секции и имеет несколько крупных клиентов, которые являются более тяжелыми для записи, чем другие. Сегодня Azure Cosmos DB перестанет прием данных по любому значению ключа секции, если оно превышает 20 ГБ данных. В этой рабочей нагрузке корпорация Майкрософт и Contoso являются крупными клиентами, и мы ожидаем, что она растет гораздо быстрее, чем другие клиенты. Чтобы избежать риска приема данных для этих клиентов, иерархические ключи секций позволяют масштабировать эти клиенты за пределы 20 ГБ. Мы можем добавить дополнительные уровни, такие как UserId и SessionId, чтобы обеспечить более высокую масштабируемость в клиентах.

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

    • Если ваш первый уровень не имеет высокой кратности, и вы достигаете ограничения 20 ГБ логических секций на ключ секции сегодня, мы рекомендуем использовать искусственный ключ секции вместо иерархического ключа секции.

Примеры использования

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

В реальном сценарии некоторые клиенты могут увеличиваться, с тысячами пользователей, в то время как многие другие клиенты меньше и имеют несколько пользователей. Секционирование /TenantId может привести к превышению ограничения хранилища Azure Cosmos DB 20 ГБ в одной логической секции. Секционирование путем /UserId выполнения всех запросов между секциями клиента. Оба подхода имеют значительные недостатки.

Использование искусственного ключа секции, который объединяет TenantId и UserId добавляет сложность в приложение. Кроме того, запросы искусственного ключа секции для клиента по-прежнему перекрестны, если только все пользователи не известны и указаны заранее.

Если у рабочей нагрузки есть клиенты с примерно одинаковыми шаблонами рабочих нагрузок, иерархический ключ секции может помочь. С помощью иерархических ключей секционирования сначала можно секционирование в TenantId, а затем в UserId. Если вы ожидаетеTenantId, что и UserId сочетание создают секции, превышающие 20 ГБ, вы можете даже секционировать дальше до другого уровня, например.SessionId Общая глубина не может превышать три уровня. Если физическая секция превышает 50 ГБ хранилища, Azure Cosmos DB автоматически разделяет физическую секцию, чтобы примерно половина данных была на одной физической секции, а половина — в другой. Фактически подсекция означает, что одно TenantId значение может превышать 20 ГБ данных, и данные TenantId могут охватывать несколько физических секций.

Запросы, указывающие либо TenantIdили и TenantId UserIdто, и другое, эффективно направляются только в подмножество физических секций, содержащих соответствующие данные. Указание полного пути к ключу секции или пути с префиксами для подсекций позволяет эффективно избежать полностью размноженного запроса. Например, если контейнер имел 1000 физических секций, но определенное TenantId значение было только в 5 физических секциях, запрос будет перенаправлен на меньшее количество соответствующих физических секций.

Использование идентификатора элемента в иерархии

Если в контейнере есть свойство с большим диапазоном возможных значений, это свойство, скорее всего, отличный выбор ключа секции для последнего уровня иерархии. Одним из возможных примеров этого типа свойства является идентификатор элемента. Системное свойство идентификатор элемента есть в каждом элементе контейнера. Добавление идентификатора элемента в качестве другого уровня гарантирует, что можно масштабировать за пределы ключа логического раздела в 20 ГБ. Вы можете масштабировать за пределами этого предела для первого или второго уровней ключей.

Например, у вас может быть контейнер для мультитенантной рабочей нагрузки, секционирующейся по TenantId и UserId. Если можно использовать одно сочетание TenantId и UserId превысить 20 ГБ, рекомендуется секционировать с помощью трех уровней ключей и в которых ключ третьего уровня имеет высокий кратность. Пример этого сценария заключается в том, что ключ третьего уровня — GUID, имеющий естественно высокий кратность. Маловероятно, что сочетание TenantId, UserIdи GUID превышает 20 ГБ, поэтому сочетание TenantId и UserId может эффективно масштабироваться за пределами 20 ГБ.

Дополнительные сведения об использовании идентификатора элемента в качестве ключа секционирования см. в обзоре секционирования.

Начало работы

Внимание

Работа с контейнерами, используюющими иерархические ключи секций, поддерживается только в следующих версиях пакета SDK. Необходимо использовать поддерживаемый пакет SDK для создания контейнеров с иерархическими ключами секции и выполнения операций создания, чтения, обновления и удаления (CRUD) или запросов к данным. Если вы хотите использовать пакет SDK или соединитель, который в настоящее время не поддерживается, отправьте запрос на наш форум сообщества.

Найдите последнюю предварительную версию каждого поддерживаемого пакета SDK:

SDK Поддерживаемые версии Ссылка для диспетчера пакетов
Пакет SDK для .NET версии 3 >= 3.33.0 https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.33.0/
Пакет SDK для Java версии 4 >= 4.42.0 https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/cosmos/azure-cosmos/CHANGELOG.md#4420-2023-03-17/
Пакет SDK версии 4 для JavaScript 4.0.0 https://www.npmjs.com/package/@azure/cosmos/
Пакет SDK для Python >= 4.6.0 https://pypi.org/project/azure-cosmos/4.6.0/

Создание контейнера с помощью иерархических ключей секций

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

Вы можете создать новый контейнер с помощью одного из следующих вариантов:

  • Портал Azure
  • SDK
  • Шаблон Azure Resource Manager
  • Эмулятор Azure Cosmos DB

Портал Azure

Самый простой способ создать контейнер и указать иерархические ключи секции — использовать портал Azure.

  1. Войдите на портал Azure.

  2. Перейдите на существующую страницу учетной записи Azure Cosmos DB для NoSQL.

  3. В меню слева выберите Обозреватель данных.

    Снимок экрана: страница новой учетной записи Azure Cosmos DB для NoSQL с выделенным параметром меню обозревателя данных.

  4. В обозревателе данных выберите параметр "Создать контейнер".

    Снимок экрана: параметр

  5. Введите /TenantIdв разделе "Новый контейнер" для ключа секции. В остальных полях введите любое значение, соответствующее вашему сценарию.

    Примечание.

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

  6. Дважды выберите " Добавить иерархический ключ секции".

    Снимок экрана: кнопка для добавления нового иерархического ключа секции.

  7. Для второго и третьего уровней подсекционирования введите /UserId и /SessionId соответственно.

    Снимок экрана: список трех иерархических ключей секций.

  8. Выберите ОК, чтобы создать контейнер.

SDK

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

// List of partition keys, in hierarchical order. You can have up to three levels of keys.
List<string> subpartitionKeyPaths = new List<string> { 
    "/TenantId",
    "/UserId",
    "/SessionId"
};

// Create a container properties object
ContainerProperties containerProperties = new ContainerProperties(
    id: "<container-name>",
    partitionKeyPaths: subpartitionKeyPaths
);

// Create a container that's subpartitioned by TenantId > UserId > SessionId
Container container = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: 400);

Шаблоны диспетчера ресурсов Azure

Шаблон Azure Resource Manager для подпартийного контейнера почти идентичен стандартному контейнеру. Единственное ключевое различие заключается в значении properties/partitionKey пути. Дополнительные сведения о создании шаблона Azure Resource Manager для ресурса Azure Cosmos DB см. в справочных материалах по шаблонам Azure Resource Manager для Azure Cosmos DB.

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

Путь Значение
paths Список иерархических ключей секций (максимум три уровня глубины)
kind MultiHash
version 2

Пример определения ключа секции

Например, предположим, что у вас есть иерархический ключ секции, состоящий из TenantIdSessionId>UserId>. Объект partitionKey будет настроен для включения всех трех значений в paths свойство, kind значения MultiHashи version значения 2.

partitionKey: {
  paths: [
    '/TenantId'
    '/UserId'
    '/SessionId'
  ]
  kind: 'MultiHash'
  version: 2
}

Дополнительные сведения об объекте см. в спецификации partitionKeyContainerPartitionKey.

Эмулятор Azure Cosmos DB

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

.\CosmosDB.Emulator.exe /EnablePreview

Предупреждение

В настоящее время эмулятор не поддерживает все функции ключа иерархии секций на портале. Эмулятор в настоящее время не поддерживает:

  • Создание контейнеров с помощью иерархических ключей секций с помощью обозревателя данных
  • Использование обозревателя данных для перехода к элементам и взаимодействию с ними с помощью иерархических ключей секций

Дополнительные сведения см. в статье Эмулятор Azure Cosmos DB.

Использование пакетов SDK для работы с контейнерами с иерархическими ключами секций

Если у вас есть контейнер с иерархическими ключами секционирования, используйте ранее указанные версии пакетов SDK для .NET или Java для выполнения операций и выполнения запросов в этом контейнере.

Добавление элемента в контейнер

Существует два варианта добавления нового элемента в контейнер с включенными иерархическими ключами секций:

  • Автоматическое извлечение
  • Вручную укажите путь

Автоматическое извлечение

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

// Create a new item
UserSession item = new UserSession()
{
    id = "f7da01b0-090b-41d2-8416-dacae09fbb4a",
    TenantId = "Microsoft",
    UserId = "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b",
    SessionId = "0000-11-0000-1111"
};

// Pass in the object, and the SDK automatically extracts the full partition key path
ItemResponse<UserSession> createResponse = await container.CreateItemAsync(item);

Вручную укажите путь

Класс PartitionKeyBuilder в пакете SDK может создать значение для ранее определенного пути к иерархическому ключу секции. Используйте этот класс при добавлении нового элемента в контейнер с включенной подпартией.

Совет

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

// Create a new item object
PaymentEvent item = new PaymentEvent()
{
    id = Guid.NewGuid().ToString(),
    TenantId = "Microsoft",
    UserId = "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b",
    SessionId = "0000-11-0000-1111"
};

// Specify the full partition key path when creating the item
PartitionKey partitionKey = new PartitionKeyBuilder()
            .Add(item.TenantId)
            .Add(item.UserId)
            .Add(item.SessionId)
            .Build();

// Create the item in the container
ItemResponse<PaymentEvent> createResponse = await container.CreateItemAsync(item, partitionKey);

Поиск по ключу и значению (точечная операция чтения) элемента

Поиск по ключу и значению (операции чтения точек) выполняется таким образом, как и контейнер, отличный от подпартий. Например, предположим, что у вас есть иерархический ключ секции, состоящий из TenantIdSessionId>UserId>. Уникальный идентификатор элемента — это GUID. Она представлена в виде строки, которая служит уникальным идентификатором транзакции документа. Чтобы выполнить чтение точки для одного элемента, передайте id свойство элемента и полное значение ключа секции, включая все три компонента пути.

// Store the unique identifier
string id = "f7da01b0-090b-41d2-8416-dacae09fbb4a";

// Build the full partition key path
PartitionKey partitionKey = new PartitionKeyBuilder()
    .Add("Microsoft") //TenantId
    .Add("8411f20f-be3e-416a-a3e7-dcd5a3c1f28b") //UserId
    .Add("0000-11-0000-1111") //SessionId
    .Build();

// Perform a point read
ItemResponse<UserSession> readResponse = await container.ReadItemAsync<UserSession>(
    id,
    partitionKey
);

Выполнение запроса

Код пакета SDK, используемый для выполнения запроса в подпартионном контейнере, идентичен выполнению запроса в контейнере, отличном от подпартий.

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

Например, рассмотрим иерархический ключ секции, состоящий из TenantId>>UserIdSessionId. Компоненты фильтра запроса определяют, является ли запрос одним секционированием, целевым межсекционным запросом или запросом на выдумку.

Query Маршрутизация
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' AND c.SessionId = '0000-11-0000-1111' Перенаправлено на одну логическую и физическую секцию , содержащую данные для указанных значений TenantId, UserIdи SessionId.
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' Перенаправляется только в целевое подмножество логических и физических секций, содержащих данные для указанных значений TenantId и UserId. Это целевой запрос между секциями, который возвращает данные для конкретного пользователя в арендаторе.
SELECT * FROM c WHERE c.TenantId = 'Microsoft' Перенаправляется только в целевое подмножество логических и физических секций, содержащих данные для указанного значения TenantId. Это целевой запрос между секциями, который возвращает данные для всех пользователей в арендаторе.
SELECT * FROM c WHERE c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' Перенаправляется во все физические секции, что приводит к выполнению размноженного запроса между секциями.
SELECT * FROM c WHERE c.SessionId = '0000-11-0000-1111' Перенаправляется во все физические секции, что приводит к выполнению размноженного запроса между секциями.

Запрос по одной секции в контейнере с подсекциями

Ниже приведен пример выполнения запроса, который включает все уровни подсекреции, эффективно выполняя запрос с одним разделом.

// Define a single-partition query that specifies the full partition key path
QueryDefinition query = new QueryDefinition(
    "SELECT * FROM c WHERE c.TenantId = @tenant-id AND c.UserId = @user-id AND c.SessionId = @session-id")
    .WithParameter("@tenant-id", "Microsoft")
    .WithParameter("@user-id", "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b")
    .WithParameter("@session-id", "0000-11-0000-1111");

// Retrieve an iterator for the result set
using FeedIterator<PaymentEvent> results = container.GetItemQueryIterator<PaymentEvent>(query);

while (results.HasMoreResults)
{
    FeedResponse<UserSession> resultsPage = await resultSet.ReadNextAsync();
    foreach(UserSession result in resultsPage)
    {
        // Process result
    }
}

Целевой запрос по нескольким секциям в контейнере с подсекциями

Ниже приведен пример запроса, который содержит подмножество уровней подсекреции, эффективно делая этот запрос целевым многосекционным запросом.

// Define a targeted cross-partition query specifying prefix path[s]
QueryDefinition query = new QueryDefinition(
    "SELECT * FROM c WHERE c.TenantId = @tenant-id")
    .WithParameter("@tenant-id", "Microsoft")

// Retrieve an iterator for the result set
using FeedIterator<PaymentEvent> results = container.GetItemQueryIterator<PaymentEvent>(query);

while (results.HasMoreResults)
{
    FeedResponse<UserSession> resultsPage = await resultSet.ReadNextAsync();
    foreach(UserSession result in resultsPage)
    {
        // Process result
    }
}

Известные проблемы и ограничения

  • Работа с контейнерами, используюющими иерархические ключи секционирования, поддерживается только в пакете SDK для .NET версии 3 в пакете SDK для Java версии 4, в пакете SDK для Python и в предварительной версии пакета SDK JavaScript. Для создания контейнеров с иерархическими ключами секций и выполнения операций CRUD или запросов к данным необходимо использовать поддерживаемый пакет SDK. Поддержка других пакетов SDK, включая Python, в настоящее время недоступна.
  • Существуют ограничения с различными соединителями Azure Cosmos DB (например, с Фабрика данных Azure).
  • Иерархические ключи секций можно указать только до трех слоев глубины.
  • Иерархические ключи секций в настоящее время можно включить только в новых контейнерах. Необходимо задать пути ключа секции во время создания контейнера и изменить их позже. Чтобы использовать иерархические секции в существующих контейнерах, создайте новый контейнер с набором ключей иерархических разделов и переместите данные с помощью заданий копирования контейнеров.
  • Иерархические ключи секций в настоящее время поддерживаются только для учетных записей API noSQL. В настоящее время API для MongoDB и Cassandra не поддерживаются.
  • Иерархические ключи секций в настоящее время не поддерживаются функцией "Разрешения". Невозможно назначить разрешение частичному префиксу пути ключа иерархической секции. Разрешения могут быть назначены только всему пути ключа логического раздела. Например, если вы секционировались TenantId по - >UserId, вы не можете назначить разрешение, которое соответствует определенному значению TenantId. Однако вы можете назначить разрешение для ключа секции, если указать как значение для TenantId , так и "UserId".

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