
本文探讨在已有大量旧数据的数据库上是否应使用迁移进行清理,明确指出迁移仅适用于结构变更,数据清理应交由 seeder 或自定义 artisan 命令完成,以保障可维护性与迁移压缩(squash)兼容性。
在现代 PHP 框架(如 Laravel 和 CodeIgniter 4)中,数据库迁移(Migrations)的核心职责是版本化、可重复、可回滚的数据库结构演进——即创建/修改/删除表、字段、索引、约束等 DDL 操作。它不是为批量数据清理或业务逻辑裁剪而设计的工具。针对您描述的场景(继承一个含 49 张表、混杂旧用户与开发数据的遗留数据库,并需精简至约 10 张核心表),错误地将数据删除、表裁剪或脏数据清洗塞入迁移,会带来严重隐患。
✅ 正确做法:职责分离,各司其职
| 操作类型 | 推荐方式 | 原因说明 |
|---|---|---|
| 删除无用表 | 手动执行 DROP TABLE 或编写一次性迁移(仅限首次清理后立即删除该迁移文件) | 若原始库从未被迁移管理,补写 down() 删除表属于“事后补救”,不可回溯;且后续团队成员拉取项目时若跳过此迁移,状态不一致。更稳妥的是:DBA 直接清理 + 在 README.md 中记录清理清单。 |
| 清空旧用户及关联数据 | 编写自定义 Artisan 命令(Laravel)或 CLI 脚本(CI4) | 数据操作具有业务语义(如“清除注册于2020年前的非激活用户”),需日志、事务控制、分批处理(避免内存溢出)。迁移中执行 DB::table('users')->where(...)->delete() 违反关注点分离,且无法被 php artisan migrate:fresh 安全触发。 |
| 删除冗余字段/重命名列 | ✅ 使用迁移(Schema::table()->dropColumn() 等) | 属于明确的结构变更,完全符合迁移设计初衷,支持 up()/down(),可纳入 CI/CD 流程。 |
? 示例:Laravel 中安全清理数据的推荐实现
-
创建专用清理命令
php artisan make:command CleanLegacyData
-
在 app/Console/Commands/CleanLegacyData.php 中实现
use Illuminate\Console\Command; use Illuminate\Support\Facades\DB;
class CleanLegacyData extends Command { protected $signature = 'db:clean-legacy {--force : Run without confirmation}'; protected $description = 'Remove obsolete tables and legacy user data';
public function handle()
{
if (! $this->option('force')) {
if (! $this->confirm('⚠️ This will delete legacy users and related data. Continue?')) {
return 1;
}
}
DB::transaction(function () {
// 分批删除旧用户(防锁表/内存溢出)
$deleted = 0;
do {
$count = DB::table('users')
->where('created_at', '<', '2021-01-01')
->limit(1000)
->delete();
$deleted += $count;
$this->info("Deleted {$deleted} legacy users...");
} while ($count === 1000);
// 清理关联表(按外键依赖顺序)
DB::table('user_profiles')->whereIn('user_id', function ($q) {
$q->select('id')->from('users')->whereNull('email');
})->delete();
$this->info('✅ Legacy data cleanup completed.');
});
}}
3. **执行命令(带确认)** ```bash php artisan db:clean-legacy # 强制执行(生产环境慎用) php artisan db:clean-legacy --force
⚠️ 关键注意事项
- 绝不将数据操作写入常规迁移:migrate:reset、migrate:fresh 或未来 migrate:reset --seed 可能意外触发数据丢失;迁移压缩(migrate:prune / schema:dump)会直接丢弃所有 up() 中的数据逻辑。
- CodeIgniter 4 的等效方案:使用 php spark db:seed LegacyCleanerSeeder(Seeder 中实现清理逻辑),而非在 Migration 类的 up() 中调用 $this->db->delete()。
- 团队协作前提:所有结构变更(如 ALTER TABLE users DROP COLUMN middle_name)必须通过迁移提交;所有数据清理脚本需在 README.md 明确标注执行时机(如“首次部署后手动运行”),并纳入部署检查清单。
- 备份!备份!备份!:任何清理操作前,务必对生产库执行完整逻辑备份(mysqldump / pg_dump),并验证备份可恢复。
总之,迁移是数据库的“结构契约”,而数据是业务的“动态资产”。用迁移管结构,用命令/Seeder 管数据——坚守这一边界,才能让您的项目在迭代中保持健壮、清晰与可协作。










