Подготовка, которая делает решардинг дешёвым: разбить данные на много логических шардов (слотов) — 1024, 16384 — и раскладывать их по небольшому числу физических серверов через таблицу «слот → сервер». Тогда рост — это перенос части слотов на новый сервер, а функция ключ → слот не меняется. Так устроены Redis Cluster, Vitess, Citus, Kafka с партициями. Без этого hash % N → hash % 2N требует переезда почти всего.
Схема переноса одного слота (или диапазона):
- снимок — скопировать данные слота на новый узел, не останавливая запись в старый;
- догонка — применять изменения, произошедшие после снимка: поток из журнала базы (CDC, логическая репликация), с позиции, с которой делался снимок;
- сверка — сравнить объёмы и контрольные суммы по диапазонам;
- переключение — на короткое время запретить запись в слот (или поставить запросы на ожидание), дождаться, пока отставание станет нулевым, переключить маршрутизацию, снять запрет. Секунды недоступности записи для одного слота;
- уборка — удалить данные со старого узла после периода наблюдения; держать возможность отката.
Двойная запись из приложения — писать в старое и новое хранилище одновременно, читать из старого, потом переключить чтение. Проблемы: нет атомарности между двумя хранилищами (одна запись прошла, другая — нет), гонки при параллельных изменениях одной записи, нужно отдельно переносить старые данные. Если выбирать её — с идемпотентными записями, фоновой сверкой и исправлением расхождений. Поток из журнала базы надёжнее: порядок изменений сохраняется.
Маршрутизация во время переноса: прокси или библиотека знает карту слотов и её версию; клиент с устаревшей картой получает ответ «перенаправление» (MOVED/ASK в Redis Cluster) и обновляет карту.
Риски и защита: нагрузка от копирования на рабочую базу (ограничивать скорость), рост отставания, дубли при повторах (идемпотентность), запросы, которые читали данные в момент переключения. Переносить по одному слоту, начиная с малонагруженных, с метриками и возможностью остановить.