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

Php 更新Laravel中大量条目的最佳方法,php,laravel,performance,Php,Laravel,Performance,我有一份来自供应商的文件,它给了我所有时间的库存更新。我想在我的数据库中每小时进行一次更新,问题是任务太重,页面在大约10秒后加载,因为它包含将近10.000个条目 以下是我的供应商提供的文件: stock.csv ref, qty XXX01, 5 XXX02, 10 XXX03, 3 XXX04, 8 XXX05, 6 ... 我得到这个文件,并使用这个神奇的库将其转换为数组: 结果是: Array 0 => ['ref' => XXX01, 'qty' => 5] 1

我有一份来自供应商的文件,它给了我所有时间的库存更新。我想在我的数据库中每小时进行一次更新,问题是任务太重,页面在大约10秒后加载,因为它包含将近10.000个条目

以下是我的供应商提供的文件:

stock.csv

ref, qty
XXX01, 5
XXX02, 10
XXX03, 3
XXX04, 8
XXX05, 6
...
我得到这个文件,并使用这个神奇的库将其转换为数组: 结果是:

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中是否有其他选项可以使其速度更快?

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

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]);
}

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

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]);
}

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

您可以通过执行

try{
    DB::transaction(function(){
        //make your updates here
    });
}catch(\Exception $e){
    //gone wrong...
});
如果没有引发异常,它将自动提交(=保存您的更改),否则它将回滚您的更改

这里有两大优势

首先,在
transaction()
方法中,与数据库写入(插入/更新)相关的所有内容都会更快

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

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

编辑


因为我们主要讨论的是纯速度,所以我没有讨论排队作业,但是包含大量数据的文件导入/导出非常适合此功能的用途。您的脚本执行速度不会更快,但您不会在等待它完成时陷入困境,它将在另一个进程中运行。关于作业的文档

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

您可以通过执行

try{
    DB::transaction(function(){
        //make your updates here
    });
}catch(\Exception $e){
    //gone wrong...
});
如果没有引发异常,它将自动提交(=保存您的更改),否则它将回滚您的更改

这里有两大优势

首先,在
transaction()
方法中,与数据库写入(插入/更新)相关的所有内容都会更快

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

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

编辑

因为我们主要讨论的是纯速度,所以我没有讨论排队作业,但是包含大量数据的文件导入/导出非常适合此功能的用途。您的脚本执行速度不会更快,但您不会在等待它完成时陷入困境,它将在另一个进程中运行。有关乔布斯的文档

谢谢 @托伊和德文郡

这是我的新代码

$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和下午1:00运行的作业计划中工作

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

谢谢 @托伊和德文郡

这是我的新代码

$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和下午1:00运行的作业计划中工作

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

谢谢

会有帮助吗?您是否尝试过不使用Excel,这将需要大量资源,而且根本不是该软件包的目的
str_getcsv
应该快得多。会有帮助吗?您是否尝试过不使用Excel,这将需要大量资源,而且根本不是该软件包的目的
str_getcsv
应该快得多。将它们组合到单个事务中肯定是一种速度提升。将它们组合到单个事务中肯定是一种速度提升。