← Назад к вопросам
Как выполнял миграцию БД
2.0 Middle🔥 151 комментариев
#ORM и Hibernate#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Миграции базы данных: практический опыт
Типовая история: миграция с PostgreSQL 10 на PostgreSQL 13
Проводил миграцию для большого e-commerce приложения с 10+ микросервисами и 2 млн. строк данных.
Фаза 1: Планирование и подготовка
Аудит текущей БД
# Проверка размера БД
psql -h prod-db -U postgres -d myapp -c "\
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
"
Создание резервной копии
# Full backup перед миграцией
pg_dump -h prod-db -U postgres myapp | gzip > backup_$(date +%Y%m%d).sql.gz
# Проверка размера
ls -lh backup_*.sql.gz
Проверка зависимостей
-- Список всех представлений, триггеров, хранимых процедур
SELECT * FROM information_schema.views WHERE table_schema='public';
SELECT * FROM information_schema.triggers;
SELECT proname FROM pg_proc WHERE pg_proc.proowner != 1;
Фаза 2: Выполнение миграции
Вариант 1: Классическое обновление (требует downtime)
# 1. Остановить приложение
docker stop payment-service billing-service order-service
# 2. Финальный backup
pg_dump -h prod-db -U postgres myapp > backup_final.sql
# 3. Остановить PostgreSQL
sudo systemctl stop postgresql
# 4. Обновить версию
sudo apt-get update
sudo apt-get install postgresql-13 postgresql-contrib-13
# 5. Migrация данных
sudo -u postgres /usr/lib/postgresql/13/bin/pg_upgrade \
-b /usr/lib/postgresql/10/bin \
-B /usr/lib/postgresql/13/bin \
-d /var/lib/postgresql/10/main \
-D /var/lib/postgresql/13/main
# 6. Запустить PostgreSQL
sudo systemctl start postgresql
# 7. Проверить целостность данных
psql -U postgres -c "SELECT version();"
# 8. Запустить ANALYZE
psql -U postgres myapp -c "ANALYZE;"
# 9. Перезагрузить приложение
docker start payment-service billing-service order-service
Вариант 2: Zero-downtime миграция (рекомендуемый)
Использование логической репликации:
# 1. Настроить новую БД (на другом сервере)
# На source DB (PostgreSQL 10)
psql -U postgres myapp -c "\
ALTER SYSTEM SET wal_level = logical;
ALTER SYSTEM SET max_wal_senders = 10;
ALTER SYSTEM SET max_replication_slots = 10;
CREATE PUBLICATION myapp_pub FOR ALL TABLES;
"
# Перезагрузить PostgreSQL
sudo systemctl restart postgresql
# 2. На новой БД (PostgreSQL 13)
# Создать subscription для репликации
psql -U postgres -d myapp -c "\
CREATE SUBSCRIPTION myapp_sub
CONNECTION 'host=10.0.1.100 user=postgres password=*** dbname=myapp'
PUBLICATION myapp_pub;
"
# 3. Проверить статус репликации
psql -U postgres myapp -c "\
SELECT relname, n_live_tup, n_dead_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC LIMIT 10;
"
# 4. Дождаться полной синхронизации (часы/дни в зависимости от размера)
psql -U postgres myapp -c "\
SELECT subname, subslotname, received_lsn
FROM pg_subscription;
"
# 5. Переключить трафик на новую БД
# В приложении обновить connection string
# OLD: jdbc:postgresql://10.0.1.100:5432/myapp
# NEW: jdbc:postgresql://10.0.2.100:5432/myapp (новая БД)
# 6. Проверить приложение
curl http://app:8080/health
# 7. Удалить subscription на новой БД
psql -U postgres myapp -c "DROP SUBSCRIPTION myapp_sub;"
# 8. Удалить publication на старой БД
psql -U postgres myapp -c "DROP PUBLICATION myapp_pub;"
Фаза 3: Миграция приложений (Flyway)
// Spring Boot конфиг
@Configuration
public class FlywayConfiguration {
@Bean
public Flyway flyway(DataSource dataSource) {
return Flyway.configure()
.dataSource(dataSource)
.locations("classpath:db/migration")
.baselineVersion("1")
.baselineDescription("Initial version")
.load();
}
}
Структура миграций:
migrations/
├── V1_0_0__Create_tables.sql # Создание базовой схемы
├── V1_1_0__Add_indexes.sql # Оптимизация
├── V1_2_0__Update_payment_schema.sql # Новая функциональность
├── V1_3_0__Add_audit_table.sql # Compliance
└── V2_0_0__PostgreSQL_13_migration.sql # Миграция версии
-- V2_0_0__PostgreSQL_13_migration.sql
-- +goose Up
-- Обновления для PostgreSQL 13 (если нужны)
-- Например, убрать deprecated функции
-- Проверить совместимость JSON типов
ALTER TABLE products
ALTER COLUMN metadata SET DEFAULT '{}'::jsonb;
-- Обновить индексы на BRIN для больших таблиц
DROP INDEX idx_events_created_at;
CREATE INDEX idx_events_created_at ON events
USING BRIN (created_at);
-- +goose Down
DROP INDEX idx_events_created_at;
CREATE INDEX idx_events_created_at ON events (created_at);
Фаза 4: Валидация данных
Проверка целостности
-- Проверка foreign key constraints
SELECT * FROM information_schema.table_constraints
WHERE constraint_type = 'FOREIGN KEY';
-- Проверка уникальности индексов
SELECT
schemaname, tablename, indexname, indexdef
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename;
-- Проверка количества строк в критических таблицах
SELECT tablename,
(SELECT count(*) FROM pg_class WHERE relname = tablename) as row_count
FROM pg_tables
WHERE schemaname = 'public';
Сравнение данных (source vs target)
# Export данных для сравнения
psql -h old-db -U postgres myapp -c "\
COPY (SELECT COUNT(*) as total FROM orders) TO STDOUT" > old_count.txt
psql -h new-db -U postgres myapp -c "\
COPY (SELECT COUNT(*) as total FROM orders) TO STDOUT" > new_count.txt
# Проверить совпадение
diff old_count.txt new_count.txt
Фаза 5: Производительность
Проверка плана запросов
// Spring Boot тест миграции
@SpringBootTest
public class MigrationTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testQueryPerformance() {
long startTime = System.currentTimeMillis();
// Критические запросы
List<Order> orders = jdbcTemplate.query(
"SELECT * FROM orders WHERE created_at > NOW() - INTERVAL '1 day'",
new OrderRowMapper()
);
long duration = System.currentTimeMillis() - startTime;
// Проверить, что миграция не ухудшила производительность
assertTrue(duration < 1000, "Query took too long: " + duration + "ms");
}
}
EXPLAIN ANALYZE
-- Анализ плана запроса на новой БД
EXPLAIN ANALYZE
SELECT o.id, o.total, p.status
FROM orders o
JOIN payments p ON o.id = p.order_id
WHERE o.created_at > NOW() - INTERVAL '1 day'
ORDER BY o.created_at DESC;
-- Если индексы не используются, пересоздать
REINDEX TABLE orders;
REINDEX TABLE payments;
Фаза 6: Откат (если что-то пошло не так)
# 1. Переключить трафик обратно на старую БД
# Обновить connection string в приложении
# 2. Проверить статус
psql -h old-db -U postgres myapp -c "SELECT version();"
# 3. Откатить миграции (если использовалась Flyway)
# Удалить новые migration файлы и переделать
# 4. Восстановить из backup если нужно
psql -U postgres myapp < backup_final.sql
# 5. Запустить тесты
mvn test
Lessons Learned
Что сделал хорошо
✅ Полный backup перед миграцией ✅ Тестирование на staging окружении ✅ Использование логической репликации для zero-downtime ✅ Подробная документация каждого шага ✅ Мониторинг производительности после миграции
Что можно улучшить
❌ Недостаточная коммуникация с ops командой ❌ Не тестировал все edge cases ❌ Миграция началась позже ночью, а не днём
Best Practices для миграций БД
- Всегда делайте backup — перед любой операцией
- Тестируйте на staging — используйте реальные данные
- Планируйте maintenance window — сообщите пользователям
- Используйте логическую репликацию — для zero-downtime
- Проверяйте целостность данных — после миграции
- Автоматизируйте с Flyway/Liquibase — версионируйте миграции
- Имейте план отката — и тестируйте его
- Мониторьте производительность — сравните до и после
Итого
Миграция БД — это сложный процесс, требующий:
- Планирования и подготовки
- Резервных копий
- Валидации данных
- Мониторинга производительности
- Плана отката
На интервью объясните реальный опыт, трудности, которые преодолели, и lesson learned. Это показывает операционный опыт и ответственность.