Статьи

Представляем секционированные коллекции для приложений MongoDB

TokuMX 1.5 не за горами . Об этой большой возможности мы кратко поговорим, когда будем говорить об изменениях репликации в 1.4 : многораздельные коллекции.

Прежде чем представить эту функцию, я хотел бы упомянуть следующее. Хотя TokuMX 1.5 не доступен на момент написания этой статьи, мы хотели бы услышать отзывы о секционированных коллекциях, которые, на наш взгляд, являются прекрасными для данных временных рядов, как я опишу ниже. Если вы хотите попробовать эту функцию, отправьте электронное письмо по адресу support@tokutek.com для предварительной версии TokuMX 1.5.

Что такое секционированная коллекция?

Секционированная коллекция аналогична секционированной таблице в реляционных базах данных. Oracle , MySQL , SQL Server и Postgres поддерживают многораздельные таблицы. Мы рады донести эту функциональность до TokuMX. Так что, если остальная часть этого блога неясна, и у вас есть друзья в офисе, которые знакомы с реляционными базами данных, вы можете попросить их предоставить дополнительную информацию :).

Тем не менее, секционированная коллекция — это коллекция, которая под оболочками разбита (или разделена) на несколько отдельных коллекций, основанных на диапазонах «ключа разделения». С точки зрения разработчика приложения, коллекция — это просто еще одна коллекция. Запросы, вставки, обновления и удаления просто работают без синтаксических изменений. Вторичные индексы и репликация также работают. Но под покровом данные будут разбиты на несколько коллекций, причем каждая коллекция будет отвечать за все данные для диапазона ключа раздела.

Если вы работаете с TokuMX 1.4, простым примером является оплог, который является многораздельной коллекцией. Любой нормальный запрос прекрасно работает в оплоге. Однако, если вы загляните в свой каталог данных, вы увидите несколько файлов .tokumx с именем «local_oplog_rs_p…». Эти файлы являются отдельными разделами, которые разбивают данные. Каждый раздел хранит диапазон полей _id в журнале операций.

Почему я должен использовать разделенную коллекцию?

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

  • Большие порции данных могут быть очень эффективно удалены путем удаления разделов. Стоимость — это выполнение «rm» для некоторых файлов в файловой системе. Это действительно быстро и эффективно.
  • Запросы, содержащие ключ раздела, могут быть изолированы от отдельных разделов и, следовательно, выполняться быстрее. Это похоже на « изоляцию запроса » для ключей шарда.

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

Вкратце: данные временных рядов. Если у вас есть приложение с временными рядами, в котором вы хотите сохранить скользящий период данных (например, за последние 6 месяцев), то использование многораздельного сбора будет целесообразным, и предпочтительнее использования индекса TTL или ограниченного сбора . В будущем сообщении в блоге я остановлюсь на этом.

Как использовать многораздельную коллекцию в TokuMX 1.5?

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

Итак, как мне создать секционированную коллекцию?

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

Для данных временных рядов этот ключ, скорее всего, будет временной меткой.

В TokuMX ключ разделения всегда является первичным ключом . Чтобы создать секционированную коллекцию, «foo» с полем отметки времени «ts», используемым для секционирования, выполните следующее:

> db.createCollection("foo", { partitioned : 1 , primaryKey : { ts : 1 , _id : 1 } })
{ "ok" : 1 }

Обратите внимание, что в TokuMX к первичному ключу должно быть добавлено поле _id, чтобы обеспечить уникальность.

Как примечание, мы не поддерживаем разделение на основе хеша, только разделение на основе диапазона.

Добавление разделов?

В TokuMX разделы могут быть добавлены только до конца. Отдельные разделы не могут быть разделены. Итак, скажем, у нас есть коллекция, которая разделяет поле _id, где все _id являются целыми числами. Предположим, у нас есть три раздела со следующими диапазонами:

  • _id <= 0
  • 0 <_id <= 1000
  • _id> 1000

С помощью этой коллекции мы не можем создать раздел с диапазоном 500 <_id <= 1000, потому что это разделит второй раздел.

Все, что мы можем сделать, это добавить новый раздел в конец и «ограничить» текущий последний раздел новым максимальным значением. Это новое максимальное значение должно быть больше или равно первичному ключу (или в данном случае _id) последнего документа последнего раздела. Таким образом, если последний документ последнего раздела имеет значение _id 2500, мы можем только те разделы, которые создают диапазон, максимум которого составляет не менее 2500.

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

> db.foo.addPartition()
{ "ok" : 1 }

В приведенном выше примере разделенная коллекция теперь будет иметь разделы со следующими диапазонами:

  • _id <= 0
  • 0 <_id <= 1000
  • 1000 <_id <= 2500
  • _id> 2500

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

> db.foo.addPartition({ _id : 3000 });
{ "ok" : 1 }

Это сделало бы коллекцию разделами со следующими диапазонами:

  • _id <= 0
  • 0 <_id <= 1000
  • 1000 <_id <= 3000
  • _id> 3000

Сбрасывать перегородки?

Сбросить разделы просто. Сначала посмотрите, что это за разделы, с помощью следующей команды оболочки:

> db.foo.getPartitionInfo()
{
       "numPartitions" : NumberLong(4),
       "partitions" : [
               {
                       "_id" : NumberLong(0),
                       "max" : {
                               "_id" : 0
                       },
                       "createTime" : ISODate("2014-05-29T01:50:15.839Z")
               },
               {
                       "_id" : NumberLong(1),
                       "max" : {
                               "_id" : 1000
                       },
                       "createTime" : ISODate("2014-05-29T01:50:27.049Z")
               },
               {
                       "_id" : NumberLong(2),
                       "max" : {
                               "_id" : 2500
                       },
                       "createTime" : ISODate("2014-05-29T01:50:30.549Z")
               },
               {
                       "_id" : NumberLong(3),
                       "max" : {
                               "_id" : { "$maxKey" : 1 }
                       },
                       "createTime" : ISODate("2014-05-29T01:50:35.903Z")
               }
       ],
       "ok" : 1
}

This lists each partition, what the maximum value that each partition may hold (thus defining the range of the partition), and the id of the partition (in the _id field). So, in the example we used for adding partitions, we have four partitions with _ids 0 through 3.

To drop a partition, we run the following command and pass the _id of the partition we want to drop. To drop partition 0, we run:

> db.foo.dropPartition(0)
{ "ok" : 1 }

Looking at the list of partitions after this operation, we see the partition is dropped:

> db.foo.getPartitionInfo()
{
       "numPartitions" : NumberLong(3),
       "partitions" : [
               {
                       "_id" : NumberLong(1),
                       "max" : {
                               "_id" : 1000
                       },
                       "createTime" : ISODate("2014-05-29T01:50:27.049Z")
               },
               {
                       "_id" : NumberLong(2),
                       "max" : {
                               "_id" : 2500
                       },
                       "createTime" : ISODate("2014-05-29T01:50:30.549Z")
               },
               {
                       "_id" : NumberLong(3),
                       "max" : {
                               "_id" : { "$maxKey" : 1 }
                       },
                       "createTime" : ISODate("2014-05-29T01:50:35.903Z")
               }
       ],
       "ok" : 1
}

This covers how to use partitioned collections. We hope users in the MongoDB ecosystem find this feature as useful as relational database users do.

In the comments section below, feel free to leave questions and/or feedback.