使用Eloquent chunk()时,随机代码执行停止

Eug*_*per 1 php mysql laravel eloquent

我有一个计划的系统清理方法,该方法将遍历“存储”表中的所有文件,选择所需的文件类型(属性照片),然后遍历每个文件,以定义数据库中是否仍存在相应的清单。如果不是,请从数据库中删除记录并删除文件本身。

现在来解决这个问题。最初我不使用chunk(),只是使用Model :: all()来选择所有内容,并且一切都很好。但是此时,我在该存储表中有200000条记录,并且由于大量内存消耗,这些操作开始崩溃。因此,我决定使用chunk()。

因此,问题在于现在它可以正常工作了,但是,在某些随机时刻(过程中间的某个地方),代码执行就像操作已完成一样停止了,因此在任何地方都没有记录错误并且任务没有完全完成完成。

您能否提出造成这种奇怪行为的原因?

public function verifyPhotos() {
    // Instantiating required models and putting them into a single array so they can be passed to a closure
    $models = [];
    $models['storage'] = App::make('Store');
    $models['condo'] = App::make('Condo');
    $models['commercial'] = App::make('Commercial');
    $models['residential'] = App::make('Residential');
    // Obtaining and processing all records from the storage chunk by chunk
    $models['storage']->where('subject_type', '=', 'property_photo')->chunk(10000, function($files) use(&$models) {
        // Going through each record in current chunk       
        foreach ($files as $photo) {
            // If record's subject type is Condo
            if ($photo->subject_name == 'CondoProperty') {
                // Selecting Condo model to work with
                $current_model = $models['condo'];
            // If record's subject type is Commercial
            } elseif ($photo->subject_name == 'CommercialProperty') {
                // Selecting Commercial model to work with
                $current_model = $models['commercial'];
            // If record's subject type is Residential
            } elseif($photo->subject_name == 'ResidentialProperty') {
                // Selecting Residential model to work with
                $current_model = $models['residential'];
            }
            // If linked listing doesn't exist anymore
            if (!$current_model->where('ml_num', '=', $photo->owner_id)->first()) {
                // Deleting a storage record and physical file
                Storage::delete('/files/property/photos/'.$photo->file_name);
                $models['storage']->unregisterFile($photo->id);
            }
        }                                       
    });
}   
Run Code Online (Sandbox Code Playgroud)

小智 5

chunk()在Eloquent中使用将为您的SQL查询添加一个限制和一个偏移量,并为每个块执行它。如果更改数据库中的数据以减少查询匹配的行,则由于偏移,您将在下一次执行中跳过匹配的行。

也就是说,如果你有9排,id = 1...9并subject_type = 'property_photo'和使用chunk(3, ...)所产生的疑问是:

select * from store where subject_type = 'property_photo' limit 3 offset 0;
select * from store where subject_type = 'property_photo' limit 3 offset 3;
select * from store where subject_type = 'property_photo' limit 3 offset 6;
select * from store where subject_type = 'property_photo' limit 3 offset 9;
Run Code Online (Sandbox Code Playgroud)

如果在subject_type = 'something'每行的每个块集内,这些行将不再匹配,并且下一个偏移量为3的查询将有效地跳过下一个3个匹配项。

也许可以像下面这样使用Collection :: each()闭包,尽管它仍然必须将整个结果集加载到集合中:

$models['storage']->where('subject_type', '=', 'property_photo')->get()->each(function ($photo) use (&$models) {
  if ($photo->subject_name == 'CondoProperty') {
    //...
  }
  //...
});
Run Code Online (Sandbox Code Playgroud)

请记住,您还可以运行DB::disableQueryLog();以节省大型数据库操作的内存。