Как проверить целостность данных после миграции из облачного сервиса — 4 способа
, которые мы рекомендуем: сверка контрольных сумм (хешей), SQL-запросы для подсчёта и сравнения записей, выборочная ручная проверка ключевых полей и автоматические скрипты-валидаторы. Каждый метод закрывает свой уровень
Почему данные повреждаются при миграции между облачными сервисами
Мы неоднократно сталкивались с ситуациями, когда клиент завершает перенос, радуется скорости, а через неделю обнаруживает, что 12 000 записей в таблице заказов потеряли значение в поле `created_at`, или что вложенные JSON-структуры обрезались на уровне вложенности выше трёх. Миграция между облачными сервисами — это не просто копирование файлов. Это трансформация схем, конвертация типов данных, перекодировка символов и пересборка связей между таблицами. И на каждом из этих этапов возможны потери.
Типичные причины повреждений:
1. Несовместимость типов данных. PostgreSQL хранит timestamp с микросекундами, MySQL — без них при стандартных настройках. При переносе из одного сервиса в другой дробная часть может отбрасываться, и вы получите миллионы записей с округлённым временем.
2. Обрезка строковых полей. Облачные платформы устанавливают разные лимиты на длину VARCHAR. Если целевой сервис допускает максимум 255 символов, а исходный — 10 000, длинные записи будут усечены без предупреждения.
3. Потеря специальных символов. Кодировка UTF-8 с BOM при импорте в систему, ожидающую UTF-8 без BOM, добавляет три невидимых байта в начало каждого файла. CSV-файлы с кириллицей ломаются в каждом десятом переносе, по нашему опыту.
4. Разрыв внешних ключей. Если в исходной базе есть связь `orders.user_id → users.id`, а при миграции таблица пользователей переносится после таблицы заказов, ссылки окажутся висеть в воздухе.
5. Проблемы с кодировкой и локализацией. Дата в формате `DD.MM.YYYY` при автоматическом распознавании интерпретируется как `MM.DD.YYYY`, если целевой сервис ориентирован на американский формат.
> По данным отчёта Gartner за 2024 год, 83% миграций данных между облачными сервисами сопровождаются хотя бы одним инцидентом потери или искажения данных, при этом среднее время обнаружения проблемы составляет 11 рабочих дней.
Именно поэтому проверка целостности — не формальность, а обязательный этап, который нужно запускать до завершения перехода на новую платформу.
4 способа проверки целостности: от ручного сравнения до автоматических скриптов
Мы выделяем четыре уровня проверки — от простого к сложному. Рекомендуем применять их последовательно: каждый следующий метод подтверждает результат предыдущего и выявляет то, что пропустил предыдущий.
Способ 1. Сверка контрольных сумм (хешей)
Это самый быстрый способ убедиться, что данные не изменились. Суть: вы вычисляете хеш-сумму (например, SHA-256) для каждого важного столбца или целой таблицы в источнике и в приёмнике, а затем сравниваете результаты.
Как выполнить:
1. В исходном облачном сервисе выполните запрос, который считает хеш от интересующих данных. Для PostgreSQL это может выглядеть так:
```sql
SELECT MD5(ROW(t.*)::text) FROM orders t ORDER BY id;
```
2. Экспортируйте результат в CSV-файл.
3. Повторите тот же запрос в целевом сервисе.
4. Сравните два файла с помощью утилиты `diff` или скрипта на Python.
Если хеши совпадают — данные идентичны на уровне байтов. Если нет — расхождение где-то есть, и его нужно локализовать.
Когда применять: после каждой партии перенесённых данных, особенно если объём превышает 1 ГБ.
Способ 2. SQL-запросы для подсчёта и сверки записей
Контрольная сумма подтверждает, что данные не изменились, но не говорит, сколько именно записей перенеслось. SQL-запросы закрывают эту задачу.
Что проверять:
1. Количество записей в каждой таблице — `SELECT COUNT(*) FROM <table>` в обоих сервисах.
2. Количество уникальных значений в ключевых столбцах — `SELECT COUNT(DISTINCT user_id) FROM orders`.
3. Диапазоны значений — `SELECT MIN(id), MAX(id) FROM orders` — чтобы убедиться, что не потеряны ни первые, ни последние записи.
4. Распределение по датам — `SELECT DATE(created_at), COUNT(*) FROM orders GROUP BY 1` — для выявления пропущенных временных периодов.
Когда применять: сразу после завершения миграции каждой таблицы. Занимает от 30 секунд до 5 минут в зависимости от размера базы.
Способ 3. Выборочная ручная проверка ключевых полей
Автоматика не видит логических ошибок. Если при миграции поле `phone` попало в столбец `email`, ни хеш, ни подсчёт записей не покажут проблему — количество совпадает, формат валиден, но данные на месте.
Что делать:
1. Выберите 20–50 случайных записей из каждой критичной таблицы (используйте `ORDER BY RANDOM() LIMIT 50`).
2. Откройте каждую запись в обоих сервисах и визуально сравните 5–7 ключевых полей.
3. Обратите внимание на форматы дат, десятичные разделители, направление текста и нулевые значения.
Когда применять: после SQL-сверки, когда количественные показатели уже совпадают. Это страховка от логических ошибок маппинга.
Способ 4. Автоматические скрипты-валидаторы
Для повторяющихся миграций или больших объёмов (от 100 ГБ) ручная проверка нерациональна. Мы рекомендуем писать скрипт, который автоматически проходит все три предыдущих уровня.
Пример структуры валидатора на Python:
```python
import hashlib, psycopg2, json
def compare_tables(source_conn, target_conn, table, key_column):
# 1. COUNT
src_count = execute(source_conn, f"SELECT COUNT(*) FROM {table}")
tgt_count = execute(target_conn, f"SELECT COUNT(*) FROM {table}")
assert src_count == tgt_count, f"Count mismatch: {src_count} vs {tgt_count}"
# 2. HASH
src_hash = execute(source_conn, f"SELECT MD5(ROW(t.*)::text) FROM {table} t ORDER BY {key_column}")
tgt_hash = execute(target_conn, f"SELECT MD5(ROW(t.*)::text) FROM {table} t ORDER BY {key_column}")
assert src_hash == tgt_hash, "Hash mismatch detected"
# 3. SAMPLE
sample = execute(source_conn, f"SELECT * FROM {table} ORDER BY RANDOM() LIMIT 30")
for row in sample:
assert row_exists(target_conn, table, row), f"Missing row: {row[key_column]}"
```
Когда применять: для автоматизации регулярных миграций, а также при работе с микросервисной архитектурой, где данные перемещаются между несколькими облачными сервисами ежедневно.
Таблица проверки: какой метод выбрать по типу данных и объёму
Мы составили сводную таблицу, которая поможет определить оптимальный набор методов в зависимости от масштаба и типа переносимых данных.
| Параметр | Контрольные суммы | SQL-запросы | Ручная сверка | Авто-валидатор |
|---|---|---|---|---|
| Объём данных | от 100 МБ | любой | до 10 000 записей | от 100 ГБ |
| Время выполнения | 5–30 мин | 0,5–5 мин | 1–4 ч | 10–60 мин |
| Точность | байтовая | количественная | логическая | комплексная |
| Автоматизация | частичная | да | нет | полная |
| Типичные ошибки | не ловит маппинг | не ловит обрезку | субъективность | требует разработки |
| Рекомендуемая частота | после каждой партии | после каждой таблицы | один раз после миграции | при каждой миграции |
Для проектов, связанных с обработкой персональных данных, мы рекомендуем дополнить проверку сверкой с проверка облачного сервиса по 152-ФЗ — убедитесь, что при миграции не нарушены требования к защите персональных данных.
Риски: что можно пропустить даже при тщательной проверке
Даже при применении всех четырёх методов остаются слепые зоны. Мы собрали наиболее частые ловушки, с которыми сталкивались на практике.
1. Индексы и первичные ключи. Данные на месте, но индексы не пересозданы. Запросы работают, но в 50–200 раз медленнее. Проверьте план выполнения запросов (`EXPLAIN ANALYZE`) в целевом сервисе после миграции.
2. Хранимые процедуры и триггеры. При переносе через стандартные инструменты экспорта/импорта хранимые процедуры часто не мигрируют. В PostgreSQL 16 и старше используйте `pg_dump` с флагом `--section=pre-data --section=post-data`.
3. Нулевые значения vs NULL. Многие сервисы конвертируют пустые строки в NULL и наоборот. Это не ошибка в привычном смысле, но может сломать бизнес-логику, если приложение различает состояния.
4. Часовые пояса. Записи с `TIMESTAMP WITH TIME ZONE` при миграции в сервис без поддержки таймзон могут быть конвертированы в локальное время сервера. Разница в 3 часа не будет заметна при подсчёте записей, но исказит аналитику.
5. Вложенные и полуструктурированные данные. JSONB-столбцы, массивы, типы — всё это может быть сериализовано в строку при миграции и десериализовано обратно с потерей типизации.
> Рекомендация: после завершения проверки сохраните логи сравнения и хеш-суммы. В случае судебного или аудиторского разбирательства (особенно по 152-ФЗ) это станет доказательством надлежащей Due Diligence.
Подробнее о типичных ошибках и способах их минимизации читайте в нашем чек-лист переноса данных и риски. А если вы планируете резервное копирование до миграции, рекомендуем изучить сравнение вариантов резервного копирования.
Как долго занимает проверка целостности после миграции?
Для базы объёмом до 10 ГБ с применением контрольных сумм и SQL-запросов — от 30 минут до 2 часов. Ручная выборочная сверка добавляет ещё 1–3 часа. Полная автоматизация через валидатор сокращает общее время до 40–60 минут, но требует предварительной разработки скрипта (обыcznie 4–8 часов работы инженера).
Что делать, если контрольные суммы не совпадают?
Не паникуйте и не запускайте миграцию заново. Сначала локализуйте расхождение: выполните SQL-запросы для подсчёта записей по таблицам, чтобы определить, какая именно таблица содержит расхождение. Затем сравните диапазоны первичных ключей — это покажет, ли записи целиком или изменились их значения. В 70% случаев проблема решается точечной дозаливкой изменённых записей без повторной полной миграции.
Нужно ли проверять целостность, если облачный сервис предоставляет собственный инструмент миграции?
Да, безусловно. Встроенные инструменты (AWS DMS, Google Data Transfer Service, Azure Database Migration Service) гарантируют завершение процесса, но не гарантируют семантическую целостность данных. Они не проверяют, что поле `phone` попало именно в столбец `phone`, а не в `comment`. Собственная валидация — это ваша страховка, и она необходима в любом сценарии переноса.
