Сегментирование базы данных В процессе чтения моего блога у вас наверняка возникал вопрос: а что же имеется в виду под фразой сегментирование базы данных? На самом деле это просто приглянувшийся мне вариант перевода термина sharding (или он же - partitioning), в качестве альтернатив можно было бы использовать партиционирование, секционирование или что-нибудь еще менее звучное, суть от этого не меняется.

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

Что это такое?

Особенно в условиях Сети данные накапливаются и запрашиваются с невероятной скоростью, рано или поздно даже самый мощный сервер перестанет справляться с задачей хранения и предоставления данных. Количество запросов в секунду, которое способен обеспечивать один сервер ограничено ничуть не меньше, чем его дисковое пространство. Когда запросы данных начинают поступать слишком интенсивно, чаще всего прибегают к наиболее тривиальному решению: реплицирование данных на несколько серверов и обработка запросов на чтение данных параллельно, а записи - лишь на одном, классическая схема master-slave. Но и у такого подхода есть предел, с ростом системы затраты вычислительных мощностей на реплицирование данных рано или поздно начнут потреблять большую часть процессорного времени, что сделает прирост производительности от простого добавления в систему дополнительных серверов минимальным. Единственным выходом из такой ситуации становится пересмотр архитектуры всей системы хранения данным, одним из возможных исходов которого и сожет стать сегментирование базы данных.

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

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

Диапазон
Самый простой и тривиальный вариант, выбирается один из параметров какой-либо записи, например идентификационный номер, и проверяется его принадлежность определенному диапазону, например все записи с ID от 0 до 999 хранятся на одном сервере, от 1000 до 1999 - на другом, и так далее.
Список
Принцип остается такой же как и при использовании диапазонов, с той лишь разницей что проверяется принадлежность параметра какому-либо списку значений (который может состоять и из одного значения), а не диапазону, например: людей можно разбить по районам проживания или их имени.
Хэш-функция
О принципах работы хэширующих функций я уже рассказывал, так что лишь вкратце опишу принцип такого подхода: для определения в каком именно сегменте хранится та или иная запись, один из ее заранее известных параметров передается хэширующей функции, возвращающей в качестве результата номер сегмента от 0 до (n-1), где - n общее количество сегментов.
Композиция
При дальнейшем росте объемов данных и нагрузке на систему можно несколько усложнить ее работу, реализовав сегментирование на основе композиции из нескольких упомянутых выше признаков.

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

Для чего это все нужно?

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

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

А как же...

...перераспределять данные?

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

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

...выполнить операцию, затрагивающию разные сегменты?

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

...реализовать все это?

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

  • GridSQL от EnterpriseDB предоставляет систему сегментирования на базе PostgreSQL (которую, кстати, не так давно под GPL опубликовали);
  • Многие реляционные системы управления базами данных, такие как MySQL и Oracle, имеют собственную встроенную систему разбиения данных на партиции;
  • В рамках проекта Hibernate разрабатывается библиотека, инкапсулирующая сегментирование данных;
  • В статье об архитектуре LiveJournal я рассказывал о спектре opensource-решений от разработчиков этого проекта для схожих задач.

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

Ещё остались вопросы по теме?