在Laravel中更新大量条目的最佳方法

时间:2018-08-08 18:43:31

标签: php laravel performance

我从供应商处获得了一个文件,该文件可以随时为我提供库存更新。我想每小时在数据库中进行一次更新,问题是任务太繁重,由于包含了近10.000个条目,因此页面加载了将近10秒。

这是我供应商提供的文件:

stock.csv

ref, qty
XXX01, 5
XXX02, 10
XXX03, 3
XXX04, 8
XXX05, 6
...

我得到了这个文件,并使用这个神话般的库将其转换为数组:Laravel Excel 结果:

Array
0 => ['ref' => XXX01, 'qty' => 5]
1 => ['ref' => XXX02, 'qty' => 10]
2 => ['ref' => XXX03, 'qty' => 3]
...


然后创建一个foreach循环来更新数据库中的每个项目
我的items表是这样的:

id, ref, (...), qty
1, XXX01, 0
2, XXX02, 3
3, XXX03, 1
4, XXX04, 2
5, XXX05, 0

这是我的代码:

// convert .csv into Array
$path = public_path('stock').'/stock.csv';
Excel::setDelimiter(',');
$data = Excel::load($path, function($reader){})->get()->toArray();
// make it in Laravel Collection
$stocks = new Collection($data);

// The treatment
foreach ($stocks as $stock){
    $item = Item::where('ref', $stock['ref'])->first();
    $item->qty = $stock['qty];
    $item->save();
}

// Too long...

Laravel中还有其他选择可以使它更快吗?

3 个答案:

答案 0 :(得分:3)

在不检索记录的情况下更新行肯定会节省很多查询:

foreach ($stocks as $stock){
    Item::where('ref', $stock['ref'])->update(['qty' => $stock['qty']]);
}

如果经常有很多相同数量的东西,将它们分组可能会更快:

foreach($stocks->groupBy('qty') as $qty => $stocks) {
    Item::whereIn('ref', $stocks->pluck('ref'))->update(['qty' => $qty]);
}

答案 1 :(得分:2)

如Devon所述,您可以直接更新而无需检索记录。显着提高更新速度的另一种方法是使用事务。

您可以执行以下操作

try{
    DB::transaction(function(){
        //make your updates here
    });
}catch(\Exception $e){
    //gone wrong...
});

如果没有异常抛出,它将自动提交(=保存您的更改),否则它将回滚您的更改。

这里有两个主要优点。

首先,在transaction()方法中,与数据库写入(插入/更新)相关的所有操作都将更快。

第二个优点是,如果出现错误(根据我的经验,通过文件进行批量插入/更新通常会出错),则不会提交您的更改:它们不会保存到数据库中。

您可以找到Laravel关于交易here的文档。

编辑

由于我们主要是在谈论纯速度,因此我没有介绍排队的作业,但是包含大量数据的文件导入/导出非常适合此功能。您的脚本执行起来不会更快,但是不会等待完成,而会在另一个过程中搁置。有关作业is available here

的文档

答案 2 :(得分:1)

谢谢你 @toyi和@Devon

这是我的新代码

$path = public_path('stocks').'/tyce_stocks.csv';
Excel::setDelimiter(',');
$data = Excel::load($path, function($reader){})->get()->toArray();
$stocks = new Collection($data);

try{
    DB::transaction(function() use ($stocks) {
        foreach ($stocks as $stock){
            DB::table('items')
                ->where('ref', $stock['ref'])
                ->update(['qty' => $stock['qty]]);
        }
    });
}catch(\Throwable $e){
    // send mail with error
};

速度更快,页面不会崩溃,我的工作安排是每天1:00 am和1:00 pm运行

$schedule->command('stock:update')->twiceDaily(1, 13);

谢谢!