最佳实践

下面是基于 Flyway 的**生产级迁移脚本最佳实践指南**,专门针对你正在开发的 **Colleen Kotlin Web 框架** 和 PostgreSQL / JDBC 场景,总结自 Spring Boot、Rails、Laravel 等成熟框架的经验。

我会从:**命名 → 原子性 → 安全性 → 可维护性 → 团队协作 → 生产安全 → 反模式** 全面讲解。

---

# 一、文件命名最佳实践(极其重要)

Flyway 标准格式:

```text
V<version>__<description>.sql
```

示例:

```text
V1__init.sql
V2__create_users_table.sql
V3__add_email_to_users.sql
V4__create_index_on_users_email.sql
```

---

## 推荐 version 格式

推荐:

```text
V1
V2
V3
```

或:

```text
V001
V002
V003
```

或团队开发推荐:

```text
V20260227_001__init.sql
```

优点:

* 避免 merge 冲突
* 保证顺序

推荐用于 Colleen:

```text
V20260227_001__create_users.sql
```

---

## description 最佳实践

使用:

```text
snake_case
```

推荐:

```text
create_users_table
add_index_to_users_email
```

避免:

```text
createUsers
users
update
```

---

# 二、每个 migration 只做一件事(最重要原则)

正确:

```sql
-- V2__create_users_table.sql

CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    email TEXT NOT NULL
);
```

错误:

```sql
CREATE TABLE users (...);
CREATE TABLE orders (...);
CREATE INDEX ...
ALTER TABLE ...
```

原因:

migration 必须:

* 可追踪
* 可回滚(逻辑上)
* 易排错

---

# 三、migration 必须是不可变的(绝对禁止修改旧 migration)

错误做法:

修改:

```text
V1__init.sql
```

正确做法:

新增:

```text
V2__add_column.sql
```

原因:

Flyway 使用 checksum 检测修改。

修改旧 migration 会导致:

```
Validate failed: checksum mismatch
```

生产环境会阻止启动。

---

# 四、永远使用 forward-only migration(最佳实践)

不要写:

```sql
DROP TABLE users;
```

而是:

```sql
ALTER TABLE users ADD COLUMN deleted BOOLEAN DEFAULT false;
```

原因:

drop 是 destructive。

production 不可恢复。

---

# 五、必须使用 IF NOT EXISTS(增强安全性)

推荐:

```sql
CREATE TABLE IF NOT EXISTS users (
    id BIGSERIAL PRIMARY KEY
);
```

推荐:

```sql
CREATE INDEX IF NOT EXISTS idx_users_email
ON users(email);
```

避免:

```sql
CREATE TABLE users (...)
```

原因:

防止:

```
relation already exists
```

---

# 六、永远为 index 命名

正确:

```sql
CREATE INDEX idx_users_email
ON users(email);
```

错误:

```sql
CREATE INDEX ON users(email);
```

原因:

方便:

* 删除
* 调试
* 维护

---

# 七、永远为 constraint 命名

正确:

```sql
ALTER TABLE users
ADD CONSTRAINT users_email_unique UNIQUE(email);
```

错误:

```sql
ALTER TABLE users
ADD UNIQUE(email);
```

---

# 八、避免 destructive migration(生产级关键原则)

避免:

```sql
DROP COLUMN email;
```

推荐:

```sql
ALTER TABLE users
ADD COLUMN email_new TEXT;
```

然后:

应用层迁移数据。

最后:

```sql
ALTER TABLE users DROP COLUMN email;
```

分多个 migration。

---

# 九、大表 migration 必须考虑锁问题(Postgres 特别重要)

危险:

```sql
ALTER TABLE users ADD COLUMN email TEXT NOT NULL;
```

这会锁表。

推荐:

```sql
ALTER TABLE users ADD COLUMN email TEXT;

UPDATE users SET email = '';

ALTER TABLE users ALTER COLUMN email SET NOT NULL;
```

分三步 migration。

---

# 十、索引必须使用 CONCURRENTLY(Postgres)

危险:

```sql
CREATE INDEX idx_users_email ON users(email);
```

会锁表。

推荐:

```sql
CREATE INDEX CONCURRENTLY idx_users_email
ON users(email);
```

注意:

Flyway migration 必须设置:

```sql
-- flyway:executeInTransaction=false
```

因为 concurrently 不能在事务中执行。

完整:

```sql
-- flyway:executeInTransaction=false

CREATE INDEX CONCURRENTLY idx_users_email
ON users(email);
```

---

# 十一、migration 必须是幂等安全的(推荐)

推荐:

```sql
CREATE TABLE IF NOT EXISTS users (...)
```

不是:

```sql
CREATE TABLE users (...)
```

---

# 十二、永远把 schema migration 和 data migration 分开

不要:

```sql
ALTER TABLE users ADD COLUMN email TEXT;

UPDATE users SET email = 'test@test.com';
```

推荐:

```text
V2__add_email_column.sql
V3__fill_email.sql
```

原因:

便于排错。

---

# 十三、不要在 migration 中依赖应用代码

错误:

```sql
INSERT INTO users VALUES (generate_uuid());
```

除非保证 DB function 存在。

migration 必须:

自包含。

---

# 十四、推荐 Colleen migration 目录结构

```text
resources/
  db/
    migration/
      V001__init.sql
      V002__create_users.sql
      V003__add_email.sql
      V004__add_index.sql
```

---

# 十五、推荐 migration 示例(生产级)

```sql
-- V001__init.sql

CREATE TABLE IF NOT EXISTS users (

    id BIGSERIAL PRIMARY KEY,

    email TEXT NOT NULL,

    created_at TIMESTAMP NOT NULL DEFAULT NOW()

);
```

---

```sql
-- V002__add_index_to_users_email.sql

-- flyway:executeInTransaction=false

CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email
ON users(email);
```

---

# 十六、团队协作最佳实践(极重要)

永远:

* 新增 migration
* 不修改旧 migration

正确:

```text
V1
V2
V3
```

错误:

```text
修改 V1
```

---

# 十七、生产环境绝对禁止使用 clean

永远:

```kotlin
.cleanDisabled(true)
```

clean 会删除所有表。

---

# 十八、Colleen 框架推荐 migration 配置

```kotlin
Flyway.configure()
    .locations("classpath:db/migration")
    .baselineOnMigrate(true)
    .baselineVersion("0")
    .validateOnMigrate(true)
    .cleanDisabled(true)
    .outOfOrder(false)
```

---

# 十九、最推荐 version 命名(Colleen 官方推荐)

```text
V20260227_001__create_users.sql
V20260227_002__add_email.sql
```

优点:

* 无冲突
* 可排序
* 可读性强

---

# 二十、最常见灾难级错误(必须避免)

修改旧 migration:

```text
V1__init.sql ← 修改它
```

正确:

```text
V2__fix.sql
```

---

# 二十一、总结(最重要 8 条)

必须遵守:

1. 永远不要修改旧 migration
2. 每个 migration 只做一件事
3. 使用 IF NOT EXISTS
4. 使用明确命名
5. forward-only migration
6. 使用 CONCURRENTLY 创建 index
7. migration 必须可重复执行(安全)
8. migration 必须可追踪

---

如果你愿意,我可以提供一个:

**Colleen 官方推荐 migration 模板**

包括:

* table
* index
* constraint
* trigger
* enum

完整生产级示例。