首页 > 解决方案 > updateOrCreate() >100k 记录

问题描述

我有一个功能,用户可以上传CSV文件(列表),以存储/更新电子邮件DB

//load CSV
$records = file($request->file('list'), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

//prepare for mass insert
foreach ($records as $email) {
    $updateOrCreate[] = [
        'email' => $email
    ];
}

DB::beginTransaction();

try {

    $toKeep = [];

    //run a loop to cjeck if email already exists if not create
    foreach ($updateOrCreate as $record) {

        $email = List::updateOrCreate([
            'email' => $record['email']
        ], $record);

        $toKeep[] = $email->id;

    }

    //delete all records that where no in new CSV
    if (count($toKeep)) {
        List::whereNotIn("id", $toKeep)->delete();
    }

} catch (Exception $e) {

    DB::rollBack();

    return response()->json([
        'message' => $e->getMessage()
    ], 422);
}

DB::commit();

这个解决方案工作正常,最多几千条记录,如果尝试上传 50k 或更多,很慢,我明白为什么很慢(查询每条记录选择/更新或插入):

foreach ($updateOrCreate as $record) {

    $email = List::updateOrCreate([
       'email' => $record['email']
    ], $record);

    $toKeep[] = $email->id;     
}

对于插入,我使用了 raw pg_copy_from,效果很好而且速度很快

$result = pg_copy_from($dbconn, 'lists (email)', $records, ',');

所以我在徘徊是否有类似的东西来检查记录是否不在列表中删除它或添加新的,更有效/更快的方式,也许像pg_copy_from.

标签: phpsqllaravelpostgresql

解决方案


据我所见,您正在以 CSV 的 AJAX 上传方式执行此操作,并且您可能在处理时出现超时。一个简单的事实是,有了这么多的记录和你正在采取的行动,这将需要很长时间。

我建议重新考虑您的方法,例如:

  • 用户上传文件
  • 文件存储在存储中的某处
  • 设置了一个新的排队作业来处理 CSV
  • 发送用户响应
  • 排队的作业在后端执行和处理(没有超时,但要注意内存不足的问题)
  • 完成后,用户会收到有关流程的通知(广播、电子邮件)

只要您管理用户期望,这应该可行。


推荐阅读