归档前须确认 created_at 和 updated_at 字段真实可用,因许多 Laravel 项目迁移或导入历史数据时未补全时间戳,导致 whereDate('created_at', ...) 等查询异常。
归档前先确认 created_at 和 updated_at 是否真实可用
很多 laravel 项目在迁移或导入历史数据时没补全时间戳,导致 wheredate('created_at', ' 查不到数据——其实不是条件写错,是字段本身为空或为 <code>'0000-00-00 00:00:00'。mysql 8.0+ 默认 strict 模式下这种值会直接报错插入失败,但老数据可能已存在。
实操建议:
- 用
SELECT COUNT(*) FROM orders WHERE created_at 先验证数据质量 - 如果大量记录时间戳异常,别急着归档,先跑一次修复:
UPDATE orders SET created_at = updated_at WHERE created_at = '0000-00-00 00:00:00' AND updated_at > '1970-01-01'; - Laravel 迁移中建副表时,显式加
$table->timestamps(),避免新表缺字段后续出问题
用 chunkById() 而不是 chunk() 避免 OFFSET 越来越慢
归档几百万行时,Order::where('created_at', 'chunk(5000, ...) 看似稳妥,但底层用 LIMIT 5000 OFFSET XXX,越往后 OFFSET 越大,查询直接卡死。尤其当主键不连续(删过数据)时,chunk() 还可能漏行或重复。
实操建议:
- 改用
Order::where('created_at', 'chunkById(5000, function ($orders) { ... }, 'id') -
chunkById()依赖主键索引,确保id是自增或有索引;如果用 UUID,得换字段或加索引 - 每次 chunk 后手动
DB::statement('SET SESSION wait_timeout = 28800;')防止长事务被 MySQL 断连
副表命名和迁移要匹配 Eloquent 的 getTable() 行为
Laravel 不会自动识别副表。比如把旧订单移到 orders_archive,但模型仍默认查 orders,硬编码表名或改 $table 属性又影响主流程。
实操建议:
- 建迁移时用
Schema::create('orders_archive', function (Blueprint $table) { ... });,保持和主表结构一致(含索引、字符集) - 归档逻辑里用原生查询或
DB::table('orders_archive')->insert(...),别走 Eloquent save(),避免触发事件、访问器等额外开销 - 如果要用模型操作副表,临时切换:
(new Order())->setTable('orders_archive')->insert($data);,但注意它不走creating等事件
归档后记得删主表数据并优化 ANALYZE TABLE
只 insert 不 delete,主表体积不变,索引统计信息还是旧的,查询计划可能继续走全表扫描。有些团队用软删除替代归档,结果磁盘爆了才发现 PG 统计信息没更新。
实操建议:
- delete 必须带相同 where 条件:
Order::where('created_at', 'delete();,别漏掉updated_at或状态判断 - delete 后立刻执行:
DB::statement('ANALYZE TABLE orders;');(MySQL)或DB::statement('VACUUM ANALYZE orders;');(PostgreSQL) - 别信 “删完自动优化”,特别是 MySQL MyISAM 或低版本 InnoDB,统计信息可能缓存数小时
归档不是搬完就完事,最麻烦的是时间戳脏数据和 delete 后忘了刷新统计信息——这两处一漏,后面查得慢、超时、主从延迟,都得回头翻日志。

















