优化数据库更新

时间:2017-10-24 18:15:00

标签: mysql laravel optimization laravel-5

我必须更新非常大的数据集,到目前为止我有这个:

protected function updateWidgetsDb($APIWidgets, $date)
{
    echo "Eager loading...";
    $widgets = Widget::where('date', $date)->get();
    echo "Done\n";
    echo "Updating...";
    foreach ($APIWidgets as $APIWidget) {
        $widget = $widgets->where('widget_id', $APIWidget->dimensions[0])->first();

        if ($widget == null)
            continue;

        $widget->update(['revenue' => $APIWidget->metrics[0]->values[0]]);
    }
    echo "Done\n";
}

$APIWidgets是一个通过外部API获取的数组。我只获取特定日期的数据,因此我只加载该数据库中已存在的数据。

widget_id字段在DB

中编入索引

修改

我有大约60k的数据,我收到的约2k数据用于更新。这些2k的每条记录都有一个ID,已经可以在现有的60k数据中找到。因此,更新后,数据总和仍应为60k,而不是62k。

目前,更新过程大约需要10分钟。

1 个答案:

答案 0 :(得分:1)

我可以提出两种方法来加速批量更新任务。我尝试重现您的问题,因此我在widgets表格中创建了一组包含widget_id,datewidget_id索引的60k * 7项目。

  1. 首先让我们更快地获得$widget。当我用

    替换$widgets->where('widget_id', $APIWidget->id)
    Widget::where('date', $date)
       ->where('widget_id', $APIWidget->id)
    

    脚本变得快了400倍。看起来使用索引widget_id, date获取2000 mysql比通过60000大小的集合搜索Laravel集合更快。查询的结果是16秒,集合的结果是6400秒。

  2. 然后让我们更快地更新。使用临时表只有一个查询更新表有一个很好的方法。因此,我创建了一个新实体WidgetUpdate,其中包含表格widget_updates和字段id, widget_id, revenue_new

    我修改了你的方法:首先我收集一系列更新,然后 我在widget_updates表中进行批量插入,毕竟我 执行单个更新查询。我的机器做了2.2秒, 快5倍。对我来说,最终加速是2000x。

    protected function updateWidgetsDb($APIWidgets, $date)
    {
      echo "Updating...";
      $updates = [];
    
      foreach ($APIWidgets as $APIWidget) {
        $widget = Widget::where('date', $date)
            ->where('widget_id', $APIWidget->dimensions[0])
            ->first();
    
        if ($widget == null)
            continue;
    
        $updates[] = ['revenue_new' => $APIWidget->metrics[0]->values[0]];
      }
    
      # inserting and updating
      WidgetUpdate::insert($updates);
      DB::statement('UPDATE widgets, widget_updates ' . 
                'SET widgets.revenue=widget_updates.revenue_new '.
                'WHERE widgets.id = widget_updates.widget_id');
    
      echo "Done\n";
    }
    
  3. 不要忘记在完成时清理临时表。