При шардинге неизбежно возникает необходимость перебалансировать данные. Точно предсказать рост объема и формы данных практически невозможно. Поэтому ребалансировка данных – такая же систематическая операция, как и хранение данных. Ее нужно планировать на этапе проектирования, а не на этапе администрирования.
Пусть данные распределены между 5 серверами. Например, это таблица личных сообщений:
**messages**: id | user_id | time | message | sender
Данные в этой таблице распределяются путем проверки user_id (остаток от деления на 5):
12345
| Остаток от деления | user_id % 5 == 1 | user_id % 5 == 2 | user_id % 5 == 3 | user_id % 5 == 4 | user_id % 5 == 0 |
|---|---|---|---|---|---|
| Номер сервера с данными |
Т.е. для получения списка личных сообщений пользователя с id = 47, необходимо выполнить такие операции:
$user_id = 47;
$server = $user_id % 5 ? : 5;
# подключаемся к нужному серверу
**$connection = connect($server);**
# выполняем обычный запрос (в рамках подключения)
$messages = query(**$connection**, “SELECT * FROM messages WHERE user_id = {$user_id}”); # без инъекций
## Выбор сервера БД под конкретного пользователя при шардинге
Если текущие сервера заняты на 70…80% это признак того, что пора расширять их количество. Сколько ставить новых серверов? Прикинуть можно очень просто:
Допустим мы поставили еще 5 серверов и теперь их у нас 10. Если говорим о записи новых данных, то тут ничего делать не нужно – просто изменить логику шардинга (остаток от деления нужно теперь считать от 10). Но для правильного получения ранее сохраненных данных, необходимо выполнить ребелансировку данных со старых серверов на новые.
Например, в старой схеме с 5 серверами сообщения пользователя с id = 47 лежали на сервере:
[**5** серверов]: user_id = 47, server_id = **2**
## остаток от деления 47 на 5 = 2
В новой схеме с 10 серверами, данные должны лежать тут:
[**10** серверов]: user_id = 47, server_id = **7**
## остаток от деления 47 на 10 = 7
Есть две стратегии. Синхронная и асинхронная.
После установки новых серверов, до включения их в работу, необходимо переместить все данные со старых узлов на новые. Это мегаскрипт, который сделает следующее:
foreach ( $users as $id )
{
# вычисляем номера серверов в старой и новой схеме
$old_server = $id % 5 ? : 5;
$new_server = $id % 10 ? : 10;
if ( $old_server != $new_server )
{
# получаем все сообщения со старого сервера
$all_messages = get_messages($old_server, $id);
# сохраняем все сообщения на новый сервер
save_messages($new_server, $id, $all_messages);
}
}
После перемещения всех данных, приложение начнет работать с новым набором серверов.
Ясно, что на время перемещения необходимо как-то знать, кто из пользователей уже обработан, а кто еще нет.
Это можно сделать путем добавления флага в табличку пользователей и сброса его перед началом перебалансирования:
**users**: id | email | ... | messages_rebalanced
Тогда, при перабалансировке необходимо будет обновлять этот флажок:
}
В приложении это позволит обеспечить нормальную работу прямо во время перемещения данных:
$user_id = 47;
if ( $user[‘messages_rebalanced’] )
{
# данные уже перемещены, читаем с нового сервера
$server = $user_id % 10 ? : 10;
}
else
{
# данные еще не перемещены, читаем со старого сервера
$server = $user_id % 5 ? : 5;
}
## по флажку мы узнаем, с какого сервера стоит читать данные для пользователя
После выполнения ребалансировки, убираем код проверки флажка (теперь он будет установлен у всех пользователей). Сбрасываем сам флажок в ноль в базе данных, чтобы не забыть его в следующий раз.
Синхронная ребалансировка подразумевает, что мы ничего не делаем с данными, пока к ним не прийдет запрос. Например, данные пользователя c id = 47 будут лежать на 2м сервере, хотя серверов уже будет 10.
Итак, добавляем новые сервера и сразу же включаем их в работу.
Если приходит запрос на запись, мы записываем данные на нужный сервер в новой схеме.
Однако, как только приходит запрос на чтение, мы проверяем старую схему и перемещаем данные со старого сервера на новый:
Допустим мы хотим прочитать данные пользователя с user_id = 47, данные которого пока еще лежат на старом сервере (это мы тоже узнаем по флажку messages_rebalanced). Тогда перед получением данных, мы выполним их перемещение:
}
# читаем все сообщения с нового сервера
$server = $user_id % 10 ? : 10;
$messages = get_messages($server, $user_id);
## перед первым чтением в новой схеме, данные будут перемещены со старого на новый сервер
Этот метод очевидно удобнее и эффективнее, чем асинхронный. Будут перемещаться только актуальные данные, кроме этого весь процесс распределится во времени.
Однако, будьте внимательны! При перемещении больших объемов данных (например, если сообщения хранятся годами и могут заниматься сотни мегабайт), лучше использовать асинхронную ребалансировку. Иначе, приложение может крайне медленно работать для пользователя во время операций перемещения.
<h2>TL;DR
Ребалансировка – часть проектирования. Ее придется делать. Готовьтесь к тому, чтобы иметь удобный механизм ребалансировки до первой необходимости. Проще использовать синхронный метод. Однако, при больших объемах перемещаемых данных стоит выбрать асинхронный.
Visual Code от Microsoft, вероятно, один из самых популярных редакторов кода. Разработчики любят его за…
Япония сама по себе — сплошной киберпанк. Это заметил даже культовый писатель жанра Уильям Гибсон,…
Сам по себе телефон Айфон 17 Про Макс – отличный подарок. У него красивая заводская…
На фоне роста спроса на ликвидность в бычьем рынке 2025 года, криптозаймы снова выходят на…
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…