You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel自定义Artisan命令:大嵌套文件与数据集的内存友好清理

自定义Artisan命令清理文件系统问题解决

问题概述

构建每日清理文件系统的自定义Artisan命令,涉及大量文件夹与用户,存在内存崩溃问题,需完成:

  1. 筛选创建时间超过50天的日期文件夹
  2. 提取嵌套的用户ID,查询用户并检查$user->isCompleted()
  3. 若用户数据已完成,删除对应目录

目录结构

存储格式:

data/<date>/<user_id>

示例:

data/2022-01-15/7482947
data/2022-01-15/7482946
data/2022-01-15/7482945
data/2022-01-16/2353234
data/2022-01-16/2353233
data/2022-01-16/2353232

现有代码

已筛选出过期日期文件夹,但无法处理用户子目录与数据库查询:

collect(Storage::directories('data/'))
  ->filter(function ($directory) {
    $directoryDate = Str::after($directory, '/');

    if (! Carbon::parse($directoryDate)->lte(now()->subDays(50))) {
      return false;
    }

    return true;
  });

解决方案

核心优化:分批处理+避免内存过载

不要一次性加载所有目录到集合,改用循环遍历+数据库chunk查询,减少内存占用。完整命令代码如下:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use App\Models\User;

class CleanupUserData extends Command
{
    protected $signature = 'cleanup:user-data';
    protected $description = 'Clean up user data directories for completed users (data older than 50 days)';

    public function handle()
    {
        $cutoffDate = now()->subDays(50);
        $dateDirectories = Storage::directories('data/');

        foreach ($dateDirectories as $dateDir) {
            $dateStr = Str::afterLast($dateDir, '/');
            
            // 跳过无效或未过期的日期目录
            try {
                $dirDate = Carbon::parse($dateStr);
                if ($dirDate->gt($cutoffDate)) continue;
            } catch (\Exception $e) {
                $this->warn("Skip invalid date directory: $dateDir");
                continue;
            }

            // 获取当前日期下的所有用户目录
            $userDirs = Storage::directories($dateDir);
            $userIds = collect($userDirs)->map(fn($dir) => Str::afterLast($dir, '/'))
                                        ->filter(fn($id) => is_numeric($id))
                                        ->toArray();

            if (empty($userIds)) continue;

            // 分批查询用户,避免内存溢出
            User::whereIn('id', $userIds)->chunk(100, function ($users) use ($dateDir) {
                foreach ($users as $user) {
                    if ($user->isCompleted()) {
                        $targetDir = "$dateDir/{$user->id}";
                        if (Storage::exists($targetDir)) {
                            Storage::deleteDirectory($targetDir);
                            $this->info("Deleted: $targetDir");
                        }
                    }
                }
            });
        }

        $this->info('Cleanup finished.');
        return 0;
    }
}

额外优化建议

  • 队列异步处理:将每个日期目录的处理任务推入队列,分时段执行,进一步降低内存压力。
  • 添加执行锁:用Cache设置命令执行锁,防止同一时间多次运行导致冲突。
  • 日志记录:记录删除操作和异常情况,便于后续排查。
  • 测试验证:先在测试环境验证逻辑,确保不会误删有效数据。

内容的提问来源于stack exchange,提问作者BigJobbies

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 22:45:39