2017-10-24 12 views
1

私は、データの非常に大きなセットを更新する必要があり、これまでのところ、私はこれを持っている:の最適化、データベースの更新

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を介してフェッチされたアレイです。特定の日付のデータのみを取得しているので、DBにすでに存在するその日付のデータのみをロードしています。

widget_idフィールドがDB

にインデックス化され

EDIT:

私は更新のために受けていますについての2kのデータとデータのおよそ60Kを持っています。これらの2kの各レコードは、既存の60kデータにすでに存在するIDを持っています。更新後、データの合計は62kではなく60kでなければなりません。

現在、更新プロセスには約10分かかります。

+0

更新セットのサイズはどのくらいですか?なぜ2行目で 'get()'を使うのですか? –

+1

'$ widgets'は雄弁なコレクションですが、後で[where()メソッド](https://laravel.com/docs/5.5/collections#method-where)を使用します。 – ljubadr

+0

@ Norgulで詳細情報を提供できますか? '$ APIWidgets'にはいくつの要素がありますか? – ljubadr

答えて

1

大量更新タスクを高速化する2つの方法を提案できます。私はあなたの問題を再現しようとしましたので、widgetsの表にwidget_id,datewidget_idのインデックスで60k * 7の項目を作成しました。

  1. まず、$widgetを速くしましょう。私は

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

    $widgets->where('widget_id', $APIWidget->id)を交換した場合、スクリプトは〜400倍の倍高速になりました。インデックスwidget_id, dateを使用した2000のmysqlフェッチは、60000サイズのコレクションによるLaravelコレクションの検索よりも高速です。結果はクエリでは16秒、コレクションでは6400秒でした。

  2. 次に、更新を高速化しましょう。テンポラリテーブルを使用して1つのクエリのみを使用してテーブルを更新するのは良い方法です。そこで、テーブルwidget_updatesとフィールドid, widget_id, revenue_newを持つ新しいエンティティWidgetUpdateを作成しました。

    私はあなたの方法を変更した:最初に、私はその後、 私はバルクがwidget_updatesテーブルに挿入すると、すべての後、私は、単一の更新クエリを実行 作る、アップデートの配列を収集します。私のマシンでは2.2秒、 は5倍高速です。私の場合、最終的なスピードアップは2000倍です。

    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"; 
    } 
    

仕上げにクリーンアップの一時テーブルに忘れないでください。

+0

あなたはなぜ私と同じサイズのセットのために600秒を得たのか説明できますか? –

関連する問題