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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Например, у вас может быть контейнер для мультитенантной рабочей нагрузки, секционирующейся по TenantId и UserId. Если можно использовать одно сочетание TenantId и UserId превысить 20 ГБ, рекомендуется секционировать с помощью трех уровней ключей и в которых ключ третьего уровня имеет высокую карта inality. Пример этого сценария заключается в том, что ключ третьего уровня — GUID, имеющий естественно высокую карта inality. Маловероятно, что сочетание 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 JavaScript. Для создания контейнеров с иерархическими ключами секций и выполнения операций CRUD или запросов к данным необходимо использовать поддерживаемый пакет SDK. Поддержка других пакетов SDK, включая Python, в настоящее время недоступна.
  • Существуют ограничения с различными соединителями Azure Cosmos DB (например, с Фабрика данных Azure).
  • Иерархические ключи секций можно указать только до трех слоев глубины.
  • Иерархические ключи секций в настоящее время можно включить только в новых контейнерах. Необходимо задать пути ключа секции во время создания контейнера и изменить их позже. Чтобы использовать иерархические секции в существующих контейнерах, создайте новый контейнер с набором ключей иерархических разделов и переместите данные с помощью заданий копирования контейнеров.
  • Иерархические ключи секций в настоящее время поддерживаются только для учетных записей API noSQL. В настоящее время API для MongoDB и Cassandra не поддерживаются.
  • Иерархические ключи секций в настоящее время не поддерживаются функцией "Разрешения". Невозможно назначить разрешение частичному префиксу пути ключа иерархической секции. Разрешения могут быть назначены только всему пути ключа логического раздела. Например, если вы секционировались TenantId по - >UserId, вы не можете назначить разрешение, которое соответствует определенному значению TenantId. Однако вы можете назначить разрешение для ключа секции, если указать как значение для TenantId , так и "UserId".

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