Ключ раздела и дизайн запросов Cosmos DB для последовательного доступа

#azure #azure-cosmosdb

#azure #azure-cosmosdb

Вопрос:

Мы хотели бы сохранить набор документов в Cosmos DB с первичным ключом EventId . Эти записи равномерно распределены между несколькими клиентами. Клиенты должны получать доступ к последним записям для подмножества клиентов по мере добавления новых документов. Документы являются неизменяемыми и должны храниться бесконечно.

Как мы должны разработать наш ключ раздела и запросы, чтобы все клиенты не попадали в одни и те же разделы и / или не использовали высокий уровень RU?

Если мы используем только CustomerId как ключ раздела, мы в конечном итоге превысим лимит в 10 ГБ для логического раздела, и если мы используем EventId , то запросы становятся неэффективными (это приведет к межсекционным запросам и высокому использованию RU, чего мы хотели бы избежать).

Другой идеей было бы сгруппировать документы в блоки. т.е. PartitionKey = int(EventId / PartitionSize) . Это приведет к тому, что все клиенты будут использовать последние разделы, что, предположительно, приведет к снижению производительности и ограничению.

Если мы используем комбинированный CustomerId ключ раздела и int(EventId / PartitionSize) , то мне непонятно, как мы могли бы избежать запроса между разделами для получения правильного набора документов.

Редактировать:

Разъяснение нескольких моментов:

  • Клиенты будут получать доступ к событиям, указав список CustomerId событий, EventId которые они получили последними, и максимальное количество записей для извлечения.
  • По этой причине использование EventId alone не будет работать хорошо, так как это приведет к перекрестному запросу раздела (т.Е. WHERE EventId > LastEventId ).
  • Вероятно, система будет записывать порядка 1 ГБ в день с шагом в 15 минут.
  • Трудно сказать, каким будет объем чтения, но я бы предположил, что, вероятно, умеренный, возможно, несколько тысяч клиентов регулярно опрашивают API.

Комментарии:

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

2. Это помогло бы предоставить некоторые подробности о наиболее распространенных или критически важных для производительности запросах, которые вы ожидаете выполнить, и ожидаемом объеме. Например, лучший ответ может быть другим, если вы ожидаете 80/20 чтения / записи против 20/80.

3. @NoahStahl Я добавил еще несколько подробностей об ожидаемых объемах.

Ответ №1:

Итак, во-первых, ограничение на размер логических разделов теперь увеличено до 20 ГБ, см. Здесь .

Вы также можете использовать EventID в качестве раздела, поскольку у вас есть ограничение на размер логического раздела в ГБ, но у вас нет ограничений на количество логических разделов. Таким образом, с использованием EventID все в порядке, вы получите двухточечное чтение, которое выполняется очень быстро, если вы запрашиваете с использованием EventID. Теперь, когда вы упомянули, что таким образом вам придется выполнять запросы между разделами, можете ли вы объяснить, как это сделать?

Однако следует иметь в виду несколько вещей: Cosmos DB на самом деле не предназначена для хранения такого рода данных на основе журналов, поскольку она хранит все на твердотельных накопителях, поэтому, пожалуйста, подсчитайте, сколько составляет ваш размер документа 1 и сколько за секунду вам нужно хранить, а затем, сколько за день, сколько за месяц.месяц. Вы можете использовать TTL для удаления из Cosmos, когда закончите, и для долгосрочного хранения сохраните его в хранилище больших двоичных объектов Azure, а для быстрого извлечения используйте Azure Search для запроса данных в большом двоичном объекте, используя CustomerID и EventID в своем поисковом запросе.

Комментарии:

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

2. если это данные только для добавления, я бы рекомендовал использовать Azure Data Explorer. Это очень быстро и, на мой взгляд, в долгосрочной перспективе дешевле, чем Cosmos DB. Но даже используя EventID в качестве ключа раздела, вы можете использовать Azure Search для индексации контейнера Cosmos DB, и использовать его очень просто. Создайте запрос в Azure Search, который вы будете вызывать через конечную точку REST с идентификатором клиента, из которого вы можете отсортировать по 10 последним событиям. Но если это данные только для добавления, тогда нет смысла использовать Cosmos или даже SQL, а Azure Data Explorer имеет больше смысла.

Ответ №2:

Как мы должны разработать наш ключ раздела и запросы, чтобы все клиенты не попадали в одни и те же разделы и / или не использовали высокий уровень RU?

Некоторое время назад я столкнулся с аналогичной проблемой, и PartitionKey с customerId datekey , например cust1_20200920 , хорошо работал для меня.

Я создал ключ даты как 20200920 (YYYYMMDD) , но вы можете игнорировать часть даты или даже месяц (cust1_202009 /cust1_2020) , в зависимости от требований вашего запроса.

Кроме того, IMO, если во время запроса есть несколько известных ключей разделов, это неплохо. Например, если вы сохраняете YYYYMM в качестве ключа раздела и хотите получить данные за 4 месяца, вы можете выполнить 4 запроса параллельно и объединить данные. Это быстрее, если у вас много клиентов, и эти ключи разделов распределены между несколькими физическими разделами.

Отдельно отметим, что Cosmos Db недавно представила аналитическое хранилище транзакционных данных, которое может быть полезно для вашего варианта использования. Подробнее об этом здесь — https://learn.microsoft.com/en-us/azure/cosmos-db/analytical-store-introduction

Комментарии:

1. Основная проблема, с которой мы столкнулись при таком подходе, заключается в следующем: Как определить, соответствует ли отсутствующий блок времени для клиента отсутствию событий, поскольку клиент обновлен, или отсутствию событий, поскольку в этом блоке времени не было событий для клиента? Я думаю, самым простым подходом было бы добавить какой-то пустой маркер раздела, но затем вам нужно поддерживать эти маркеры по мере добавления новых клиентов в систему.

2. Как вы генерируете EventID? Если вы хотите использовать запрос типа WHERE EventID> lastEventId, идентификатор события должен быть числом. В этом случае вы можете разделить EventID с соответствующим коэффициентом и использовать его для создания ключа раздела.

Ответ №3:

Один из подходов заключается в использовании нескольких контейнеров Cosmos в качестве «горячих / холодных» уровней с различным разделением. Мы могли бы использовать два контейнера:

  • Recent : все записи и все запросы для последних элементов идут сюда. Разделен на CustomerId .
  • Archive : все элементы копируются сюда для долгосрочного хранения и доступа. Разделение на CustomerId промежуток времени (например, раздел на календарный месяц)

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

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

Эта схема должна обеспечивать эффективные запросы к последним элементам Recent и безопасное долговременное хранение Archive с разумной Archive эффективностью запросов при заданном диапазоне дат. Основным недостатком является две записи для каждого элемента (по одной для каждого контейнера), но это компромисс для эффективного опроса. Целесообразность этого компромисса, вероятно, лучше всего определить путем моделирования нагрузки и наблюдения за производительностью.