大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
迁移与 Flyway
这一课你将能做到
- 说明 Flyway versioned migration 的文件命名与「已执行不可改」规则
- 对照本站
backend/src/main/resources/migration/(或db/migration)理解演进方式 - 为 PG 练习库写出
V1__init_app_schema.sql级别的首条迁移,并知道与 MySQL 方言分文件
为什么要学
手改库结构无法协作:谁改了什么、生产有没有跑过、回滚如何追踪,都会糊成一团。Flyway(或 Liquibase)把 schema 变更变成版本化脚本,Spring Boot 启动时可自动迁移。
本站用迁移目录管理 MySQL 变更。学 PG 时,逻辑相同、SQL 方言不同——自增、布尔、注释写法都要改。官方 Flyway 文档与 PostgreSQL DDL 章是本课对标。
先建立图像
resources/db/migration/
V1__init_schema.sql ← 只跑一次,记入历史表
V2__add_track_metadata.sql
V3__index_metadata_gin.sql
启动应用 → Flyway 读 flyway_schema_history
→ 按版本号执行「还没跑过」的脚本
→ 记录成功校验和
| 规则 | 含义 |
|---|---|
V{数字}__{描述}.sql | 版本化迁移命名 |
| 已成功执行的脚本 | 禁止改内容;只能新增更高版本 |
| 校验和 | 文件被改会启动失败(防漂移) |
| baseline | 对已有库可标记基线再往前迁 |
跟着做
步骤 1:浏览本站迁移习惯
打开仓库中的迁移目录(常见为 backend/src/main/resources/migration/ 下 V*.sql)与 schema.sql。笔记:
- 基线 schema 与增量
V*如何分工 - 某次为
cf_track加列的脚本长什么样(MySQL 方言)
步骤 2:理解 Spring 配置意图
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
依赖:spring-boot-starter-flyway + 对应 JDBC 驱动。实验 PG 时,locations 可指到 classpath:db/migration-pg,与 MySQL 脚本隔离。
步骤 3:编写 PG 版 V1
在练习目录创建(路径按你的实验模块调整):
-- V1__init_app_schema.sql (PostgreSQL)
CREATE SCHEMA IF NOT EXISTS app;
CREATE TABLE app.cf_track (
id BIGSERIAL PRIMARY KEY,
slug VARCHAR(64) NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
category VARCHAR(32) NOT NULL DEFAULT 'language',
level VARCHAR(16) NOT NULL DEFAULT 'beginner',
sort_order INT NOT NULL DEFAULT 0,
is_member BOOLEAN NOT NULL DEFAULT FALSE,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_track_slug UNIQUE (slug)
);
CREATE TABLE app.cf_lesson (
id BIGSERIAL PRIMARY KEY,
track_id BIGINT NOT NULL REFERENCES app.cf_track (id) ON DELETE CASCADE,
slug VARCHAR(64) NOT NULL,
title VARCHAR(255) NOT NULL,
order_index INT NOT NULL DEFAULT 0,
content_md TEXT,
status VARCHAR(16) NOT NULL DEFAULT 'draft',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_lesson_track_slug UNIQUE (track_id, slug)
);
CREATE INDEX idx_track_metadata_gin ON app.cf_track USING GIN (metadata);
可先在 psql 手动执行验证,再纳入 Flyway。
步骤 4:写一条增量 V2
-- V2__add_track_source_type.sql
ALTER TABLE app.cf_track
ADD COLUMN IF NOT EXISTS source_type VARCHAR(16) NOT NULL DEFAULT 'imported';
对比 MySQL 版常见写法:TINYINT(1)、无 IF NOT EXISTS 等差异——不要把同一文件指望两边跑通。
步骤 5:模拟「禁止改历史」
- 假设 V1 已在历史表登记
- 若回头改 V1 加一列 → 校验和失败
- 正确做法:新增
V3__…sql
把这条规则写进笔记,比任何工具配置都重要。
步骤 6:注释方言
-- PostgreSQL 列注释
COMMENT ON COLUMN app.cf_track.slug IS '对外 URL 键';
-- MySQL 常在列定义写 COMMENT '…'
团队选一种引擎后,注释风格跟随方言。
讲清楚要点
- 迁移是代码:评审、CI、与发版绑定;禁止「只在生产手改」。
- 因为 校验和防漂移,所以 已上线脚本只追加新版本。
- 方言分目录或分仓库:MySQL 与 PG 脚本分开,避免条件分支宏变成难维护怪物。
- 本地可重建:开发库可丢数据重建;生产迁移必须可转发、可观察。
- Flyway 不管数据修复的全部哲学:大批量回填可另脚本,注意锁与窗口。
- 与本站关系:学完能读懂现有
V*.sql;PG 练习用自己的 migration-pg,不替换生产链。
你可能会懵的地方
-
schema.sql和 Flyway 谁说了算?
视项目约定:有的用 schema 初始化再 baseline;有的纯 Flyway。入门先统一「增量都进 V*」。 -
IF NOT EXISTS是否永远正确?
便于重跑实验;严格迁移有时故意失败以暴露环境漂移。团队定风格。 -
undo 脚本?
Flyway Community 以向前迁移为主;回滚靠备份或写反向 Vn。别假设点一下就时空倒流。 -
多服务共用一库?
迁移所有权要清晰,避免两服务抢同一历史表。 -
Liquibase 呢?
同属迁移工具;本课以 Flyway 为例,概念可迁移。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 修改已执行的 V1 | 校验失败/环境不一致 | 新增 V2 |
| MySQL 与 PG 共用一份 SQL | 语法处处炸 | 分目录分方言 |
| 生产关 Flyway 手改 | 无法审计 | 保持启用并走脚本 |
| 迁移里做超长锁表操作无窗口 | 业务卡住 | 拆步、在线 DDL 策略、低峰 |
| 把密钥写进迁移文件 | 泄露 | 用环境变量/密钥管理 |
自测三问
- 为什么不能改已经在
flyway_schema_history里成功的脚本内容? - PG 的
BOOLEAN/JSONB在迁移里相对本站 MySQL 意味着什么? - 如何避免实验 PG 迁移误伤生产 MySQL 脚本目录?
小结一句话
Flyway 让 schema 像代码一样版本化;换 PostgreSQL 时主要改 SQL 方言并隔离脚本目录。