生产环境默认禁止php artisan migrate,因高风险操作(如锁表、超时、结构错乱)需显式加--force确认;执行前必须备份、避峰、预发验证,否则易引发p1故障。

生产环境执行迁移必须加 --force,否则默认被禁用;不备份、不避峰、不验证就跑 php artisan migrate,等于直接在心脏上动刀。
为什么生产环境默认禁止 migrate
Laravel 在生产环境(APP_ENV=production)下硬性拦截 php artisan migrate,是为了防止误操作引发锁表、超时或结构错乱。它不是“怕你执行”,而是怕你没意识到后果——比如一个 change() 在百万级用户表上执行 ALTER,可能卡住写入十几秒甚至更久。
常见现象:Nothing to migrate. 看似成功,其实是被静默跳过;或者直接报错 Migration not allowed in production。
- 必须显式加
--force才能绕过这层保护:php artisan migrate --force - 这个开关不解决底层风险,只解除命令拦截——锁表、慢查询、外键失败等问题照旧发生
- 如果你的部署脚本里没写
--force,那线上根本没执行任何新迁移
执行前必须做的三件事
跳过其中任意一项,都可能让一次小修改变成 P1 故障。
-
备份数据库:不是“有备份习惯”就行,是这次迁移前必须触发一次完整逻辑备份(如
mysqldump或云厂商快照),且确认可恢复 -
检查执行窗口:避开流量高峰(如早 9 点、晚 8 点),选凌晨或低峰期;如果业务不允许停写,要考虑用 pt-online-schema-change 等工具替代原生
change() -
先在预发/测试库跑通:特别是含
change()、外键、JSON 字段索引的迁移,MySQL 版本差异(如 5.7 vs 8.0)会导致行为不一致
哪些迁移操作在生产最危险
不是所有 up() 都平等。有些看似简单,实则暗藏阻塞点。
-
Schema::table('orders')->dropColumn('coupon_code'):MySQL 会重建整表,订单量大时不可控 -
$table->string('description', 2000)->change():从VARCHAR(255)扩到2000,若字段已有数据且引擎是 InnoDB,仍可能锁表 -
add_foreign_key_to_orders_table但依赖的users表还没建:时间戳顺序错位,报Failed to open the referenced table,且migrate:rollback可能无法回退到最后一步 - 任何没写
down()的迁移:一旦出错,只能手动修复,migrate:rollback失效
执行后必须验证的点
看到 Migrated: 不代表完事。真实风险常在“看起来成功”之后才暴露。
- 查
migrations表,确认新记录已插入,且batch值递增 —— 防止因事务中断导致部分写入 - 连上 DB,运行
DESCRIBE users;或SHOW CREATE TABLE users;,核对字段类型、长度、约束是否和迁移代码一致(比如nullable()是否生效、unique()索引是否真建上了) - 触发一条真实业务写入(如注册一个测试用户),看
created_at是否自动填充、软删除字段是否正常工作——有些问题只在 Eloquent 实际调用时浮现 - 检查日志:
storage/logs/laravel.log里有没有SQLSTATE[HY000]或警告,尤其是涉及doctrine/dbal的字段变更提示
最易被忽略的是:迁移成功了,但应用代码还没适配新字段(比如忘了加 $fillable 或没改 API validator),结果数据写不进新列,错误却藏在业务层里,等用户投诉才被发现。











