这是个典型的“看起来很小,实际会影响长期架构”的决策点

每次新起一个项目,都会有个“要不要用数据库枚举”的问题。这个问题的答案可能会影响未来几年的开发效率和维护成本。


一、先给结论

枚举(ENUM)适合:稳定、小范围、强约束的字段
不适合:会变化、需要扩展、涉及业务逻辑的字段


二、用枚举的好处

当然也不是说它一无是处,还是有一些好处的:

1️⃣ 数据层强约束(防脏数据)

status ENUM('draft', 'published', 'archived')

数据库直接帮你兜底:

  • ❌ 不可能插入 abc
  • ❌ 不可能拼错 publised

👉 比 varchar 靠谱很多


2️⃣ 节省存储

比如在 MySQL 里 ENUM 实际存的是 索引值

'draft' → 1
'published' → 2

所以:

  • 占用空间小
  • 查询快一点(但别神话这个优势)

3️⃣ 语义清晰(数据库层)

你一看表结构就知道:

status ENUM(...)

status VARCHAR(50) 更有“约束力”。


三、坏处(这是重点)

1️⃣ ❌ 修改成本非常高(致命点)

加一个值:

ALTER TABLE posts MODIFY status ENUM(...)

问题:

  • 锁表(大表直接炸)
  • migration 很重
  • CI/CD 风险高

👉 这是最大的问题,没有之一


2️⃣ ❌ 不利于扩展(你这个项目很可能会踩)

比如你现在:

draft / published

未来可能变:

draft / reviewing / rejected / scheduled / published

甚至:

  • 多语言
  • 自定义状态
  • SaaS 不同租户不同状态

👉 ENUM 直接卡死你


3️⃣ ❌ 和业务逻辑耦合太深

你在:

  • 后端
  • 前端
  • API

都要同步这套枚举。

一旦改:

👉 全链路改动


4️⃣ ❌ 不利于生态

在开源社区,数据库 ENUM 可能会导致:

  • 贡献者不敢改
  • 维护者不敢改

因为改了可能会破坏现有用户。

还有一个隐藏风险是:不同数据库对 ENUM 的支持不一致。

  • MySQL 有原生 ENUM 类型
  • PostgreSQL 也支持,但语法不同、迁移流程更复杂
  • SQLite 没有 ENUM,通常会退化为 TEXT

所以使用 ENUM 将你的数据层约束与数据库厂商绑定,移植成本高。

例如 Laravel 推荐:

  • PHP Enum(8.1+)
  • 常量类
  • 配置驱动

而不是 DB ENUM。

现在数据库都已经支持 ENUM,但编程语言本身的枚举类型更灵活:你可以把业务规则放到代码层控制、版本管理和测试,避免频繁修改迁移文件。

比如:

enum PostStatus: string {
    case Draft = 'draft';
    case Published = 'published';
}

👉 更灵活、可扩展、可测试


四、在 Laravel 项目里的“最佳实践”

🥇 推荐方案(最通用)

  • ✅ 数据库用 varchar
  • ✅ 业务用 PHP Enum
$table->string('status');
enum PostStatus: string {
    case Draft = 'draft';
    case Published = 'published';
}

Model:

protected $casts = [
    'status' => PostStatus::class,
];

👉 优点:

  • DB 不锁死
  • 代码强类型
  • 易扩展
  • 易维护

迁移提示:从 ENUM 到 varchar

如果你当前正用 ENUM,可以按以下步骤安全迁移:

  1. 新增 varchar 字段并写入 enum 值(不更新旧字段)
  2. 应用代码改为读写新字段,并在测试中验证行为一致
  3. 执行 CHECK 约束(可选)
  4. 删除老 enum 字段

这种方式避免一次性 ALTER TABLE 卡表,同时保留回滚路径。

平衡本地化与扩展的场景建议

  • 推荐:状态类型由应用层定义(Enum、常量类、字典表)
  • 可接受:极其稳定、完全不可变的技术字段(如 genderdevice_type
  • 不推荐:经常变化或多租户自定义的业务字段

🥈 如果需要更强扩展性

通常像 SaaS 产品,状态可能会被客户自定义,这时候:

👉 用“字典表

statuses
- id
- code
- name
- tenant_id(可选)

👉 适合:

  • 可配置状态
  • 多租户
  • 后台可编辑

🥉 ENUM 适合的场景

只有这种情况我才建议你用:

  • 极其稳定
  • 不会扩展
  • 纯技术字段

比如:

gender: male / female
device_type: ios / android / web

五、一句经验总结

ENUM 是“现在很爽,未来很痛”的设计