Все о разделении данных в базах данных

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

Что такое разделение данных?

Данные в базах данных разбиты на разделы. Каждый раздел определяется таким образом, что каждый фрагмент данных находится ровно в одном разделе. Узел может содержать несколько разделов. Разделение выполняется для обеспечения масштабируемости, поскольку все данные могут не уместиться в одном узле. Разные разделы могут находиться на разных узлах. Каждый узел может обслуживать запросы со своим собственным разделом. Этот процесс также известен как сегментирование. Наряду с разделением каждый раздел реплицируется на несколько узлов для обеспечения доступности.

Перекошенные перегородки и горячие точки

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

Способы разделения данных

Мы обсудим три различных способа разделения данных:

  1. Случайное распределение
  2. Разбиение по диапазону ключей
  3. Разбиение по хешу ключей

Случайное распределение ключей

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

Разбиение по диапазону ключей

Другой подход может заключаться в том, чтобы разделить ключи на разные диапазоны и назначить каждый диапазон разделу. Например, ключи со значениями A – E могут находиться в разделе 1, F – K - в разделе 2, L – R - в разделе 3 и так далее. Диапазон должен быть равномерным. Например, данные с ключами F, G, H могут быть меньше и приводят к формированию диапазона (F-K) с большим количеством ключей в нем.

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

Разбиение по хешу ключей

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

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

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

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

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

2. Разделение вторичных индексов по элементам (или глобальным индексам). Здесь есть глобальный вторичный индекс, который охватывает все данные. С каждым ключом он сохраняет раздел, в котором он находится. Поскольку полный индекс может не соответствовать одному узлу, глобальный индекс также может быть разделен на разные узлы. Например, индекс с ключами A-R может находиться в узле 1 и опираться на узел 2. Глобальные индексы увеличивают задержку записи, но обеспечивают более быстрое чтение.

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

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

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

Ребалансировка может выполняться автоматически или вручную.

Стратегии ребалансировки

  1. Как этого не делать: Почему бы не назначить ключи разделу по хешу (ключу) по модулю N, где N - количество узлов? Поскольку при изменении N потребуется много перестановок и перемещений разделов.
  2. Фиксированное количество разделов. Определите количество разделов. Количество разделов может быть больше количества узлов (N). Каждый узел будет содержать несколько разделов. Когда узел удаляется или добавляется, некоторые из существующих разделов могут перемещаться на новый узел.
  3. Динамическое разбиение. При фиксированном разбиении разделы не адаптируются к увеличению объема данных. При динамическом разбиении вы можете выбрать начальное количество разделов. По мере того как размер раздела увеличивается с увеличением объема данных, он разделяется для создания нового раздела. Точно так же он сжимается при удалении данных. После разделения один из разделов может быть назначен другому узлу.
  4. Разделение пропорционально узлам: здесь каждый узел содержит фиксированное количество разделов. Размер каждого раздела увеличивается пропорционально размеру набора данных. Когда добавляется новый узел, некоторые из существующих разделов разделяются пополам, а одна половина перемещается в новый узел. Границы раздела выбираются случайным образом и, следовательно, требует разделения по хешу ключа.

Ресурсы

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

Надеюсь, статья вам понравилась!