← Назад к вопросам

Как выполнял миграцию БД

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 для миграций БД

  1. Всегда делайте backup — перед любой операцией
  2. Тестируйте на staging — используйте реальные данные
  3. Планируйте maintenance window — сообщите пользователям
  4. Используйте логическую репликацию — для zero-downtime
  5. Проверяйте целостность данных — после миграции
  6. Автоматизируйте с Flyway/Liquibase — версионируйте миграции
  7. Имейте план отката — и тестируйте его
  8. Мониторьте производительность — сравните до и после

Итого

Миграция БД — это сложный процесс, требующий:

  • Планирования и подготовки
  • Резервных копий
  • Валидации данных
  • Мониторинга производительности
  • Плана отката

На интервью объясните реальный опыт, трудности, которые преодолели, и lesson learned. Это показывает операционный опыт и ответственность.