Сверка данных: как доказать, что миграция прошла корректно
Вы только что завершили миграцию данных. Скрипт отработал без ошибок. Логи чистые. Команда готова двигаться дальше. Но где-то в глубине души вас гложет сомнение: а действительно ли всё сработало? Все ли строки перенесены? Не произошло ли молчаливого искажения значений?
Именно в этот момент большинство команд осознают: успешное выполнение миграции — это не то же самое, что корректная миграция. Скрипт может завершиться с кодом 0 и при этом выдать неверные данные. Фильтр может исключить строки, которые должен был включить. Преобразование типов может обрезать значения без единой ошибки. База данных не сообщит вам об этих проблемах, если вы явно не спросите.
Именно для этого и нужна сверка (reconciliation). Это процесс сравнения данных до и после миграции, чтобы доказать: ничего не потеряно, не изменено и не повреждено. Это финальная контрольная точка перед тем, как объявить миграцию завершённой.
Почему скрипты без ошибок — это не гарантия
Неудобная правда о миграции данных: корректность и безошибочное выполнение — это разные вещи. Скрипт миграции может отработать идеально с технической точки зрения, но выдать неверный результат.
Рассмотрим типичный сценарий: вы мигрируете таблицу с условием WHERE, которое отфильтровывает неактивных пользователей. Скрипт выполняется, строки успешно вставляются, старая таблица выводится из эксплуатации. Через несколько недель кто-то замечает, что группа активных пользователей пропала. Фильтр оказался слишком жёстким, или условие было неверным. Скрипт ни разу не упал, но данные неверны.
Ошибки в логах не фиксируют такие проблемы. СУБД не знает, что отсутствие строки — это ошибка. Она знает только, что INSERT выполнился успешно. Единственный способ поймать такие молчаливые сбои — напрямую сравнить исходные данные с целевыми.
Практический подход к сверке
Сверка не обязана быть сложной. Самый простой и эффективный метод — сравнение контрольных сумм (checksum). Только вместо файлов мы вычисляем хеши для пакетов данных.
Вот как это работает для миграции таблицы:
- Прочитайте пакет строк из исходной таблицы.
- Вычислите хеш всего пакета (например, MD5 или SHA256 от конкатенации значений всех колонок).
- Прочитайте тот же пакет из целевой таблицы, используя ту же сортировку, и вычислите такой же хеш.
- Сравните два хеша.
Если хеши совпадают — пакет идентичен. Если нет — вы точно знаете, в каком пакете проблема, и можете исследовать конкретный диапазон строк.
Для больших таблиц обработка пакетами обязательна. Не стоит загружать миллионы строк в память за раз. Размер пакета от 1 000 до 10 000 строк хорошо работает для большинства баз данных. Можно запускать сравнение параллельно для нескольких пакетов, чтобы ускорить процесс.
Следующая блок-схема иллюстрирует процесс сверки по пакетам:
Вот конкретный SQL-запрос, реализующий этот подход для двух таблиц:
-- Сравнение количества строк и контрольных сумм между исходной и целевой таблицами
WITH source_checksums AS (
SELECT
COUNT(*) AS row_count,
MD5(STRING_AGG(CAST(column1 AS TEXT) || '|' || CAST(column2 AS TEXT) || '|' || CAST(column3 AS TEXT), ',' ORDER BY id)) AS batch_hash
FROM source_table
),
target_checksums AS (
SELECT
COUNT(*) AS row_count,
MD5(STRING_AGG(CAST(column1 AS TEXT) || '|' || CAST(column2 AS TEXT) || '|' || CAST(column3 AS TEXT), ',' ORDER BY id)) AS batch_hash
FROM target_table
)
SELECT
'Несовпадение количества строк' AS issue
FROM source_checksums, target_checksums
WHERE source_checksums.row_count <> target_checksums.row_count
UNION ALL
SELECT
'Несовпадение контрольной суммы' AS issue
FROM source_checksums, target_checksums
WHERE source_checksums.batch_hash <> target_checksums.batch_hash;
Этот запрос вычисляет единую контрольную сумму по всем строкам каждой таблицы (с фиксированной сортировкой) и сравнивает как количество строк, так и хеш. Если хотя бы одно различается, запрос возвращает чёткое указание на проблему.
Что ещё проверить помимо контрольных сумм
Контрольные суммы ловят большинство проблем, но это не единственное, что стоит проверить. Несколько дополнительных проверок повышают уверенность без лишних усилий.
Количество строк. Это самая простая проверка. Количество строк в целевой таблице должно совпадать с исходной. Если количество различается — в логике миграции ошибка.
Null-значения. При миграции иногда null-колонки заменяются значениями по умолчанию или наоборот. Сравните количество null на колонку между источником и приёмником. Расхождение часто указывает на проблему преобразования типов или неверно применённое ограничение DEFAULT.
Распределение значений. Выберите несколько важных колонок и сравните их распределение. Например, если в исходной таблице 1 000 пользователей со статусом "active" и 500 со статусом "inactive", в целевой должно быть то же самое. Существенное различие указывает на ошибку в фильтре или логике трансформации.
Граничные случаи. Проверьте конкретные строки, которые заведомо сложны: строки со спецсимволами, очень длинные строки, даты у границ диапазона, отрицательные числа. Если миграция обработала их корректно, это хороший знак.
Интеграция сверки в пайплайн
Сверка не должна быть разовой ручной задачей, которую кто-то вспоминает запустить после ночной миграции. Она должна быть автоматизирована и встроена в пайплайн развёртывания.
Напишите скрипт сверки, который запускается после завершения миграции и обратного заполнения (backfill). Скрипт должен:
- Подключаться к исходной и целевой базам данных.
- Выполнять пакетное сравнение контрольных сумм.
- Проверять количество строк, null-значений и распределение значений.
- Формировать детальный отчёт о любых расхождениях.
- Отправлять уведомление (email, Slack или другой канал) с результатами.
Если сверка пройдена, пайплайн переходит к следующему шагу. Если нет — пайплайн останавливается и оповещает команду. Это предотвращает попадание неверных данных в продуктивную среду незамеченными.
Автоматизация сверки также делает её повторяемой. Каждая миграция проходит один и тот же процесс верификации. Вам не нужно полагаться на то, что кто-то вспомнит запустить скрипт или проверить нужные метрики. Пайплайн обеспечивает это принудительно.
Чем сверка не является
Сверка не заменяет пробные прогоны (dry runs) или стратегии обратного заполнения. Каждый шаг служит своей цели.
- Пробные прогоны проверяют, что логика миграции работает без воздействия на продуктивные данные.
- Обратное заполнение выполняет фактическую передачу данных управляемыми порциями, чтобы минимизировать влияние.
- Сверка доказывает, что обратное заполнение дало корректные результаты.
Считайте сверку финальным контролем качества. Она подтверждает, что все предыдущие шаги сработали как надо. Если сверка пройдена — данные готовы. Если нет — возвращаетесь, исправляете проблему и запускаете миграцию заново.
Практический чек-лист сверки
При настройке сверки для следующей миграции используйте этот краткий чек-лист:
- Сравнение контрольных сумм по пакетам (1 000–10 000 строк на пакет)
- Совпадение количества строк между источником и приёмником
- Совпадение количества null по каждой колонке
- Совпадение распределения значений для ключевых колонок
- Проверка граничных случаев (спецсимволы, граничные значения)
- Автоматизированный скрипт, встроенный в пайплайн
- Уведомление при сбое с детальным отчётом о расхождениях
Вывод
Миграция не завершена, пока вы не доказали, что данные корректны. Безошибочного выполнения недостаточно. Сверка даёт вам это доказательство путём прямого сравнения исходных и целевых данных. Автоматизируйте её, запускайте каждый раз и относитесь к проваленной сверке так же, как к упавшему тесту: остановитесь, исследуйте и исправьте, прежде чем двигаться дальше.