2017-10-23 13 views
0

私は外部のAPIからデータを取得し、その後DBにコミットし、コードを持っている:私はLaravelデータベースの最適化

protected function saveWidgetsToDatabase($widgetsDaily, Boost $boost, $date) 
{ 
    echo "Saving widgets to DB... "; 

    $widgets = Widget::all(); 
    foreach ($widgetsDaily as $widgetDaily) { 
     $existingWidget = $widgets 
      ->where('widget_id', $widgetDaily->id) 
      ->where('date', $date) 
      ->first(); 

     if ($existingWidget === null) 
      $boost->widgets()->save(new Widget([ 
       ... 
      ])); 
     else 
      $existingWidget->update([ 
       ... 
      ]); 
    } 
} 

関係は1 Boostは多くのWidgetsを持っていることです。今、私が直面している問題は、同じ日付とIDを持つ場合にのみウィジェットを更新する必要があるため、ボトルネックDBの保存/更新です。そうでなければ、新しいものを作成する必要があります。

私たちは数千ものレコードについて話していますので、whereの節はかなり集中していると思います。

バッチ・セーブをしたいと思っていましたが、私はそれを作っていませんでした。

これを高速化する可能性はありますか?

+0

'widget_id'と' date'のインデックスはありますか? – aynber

+0

いいえ、本当は...助けてくれますか? – Norgul

+0

はい。索引付けは、データベースが情報をより迅速に見つけ出すのを助け、 'where'クエリを高速化します。 – aynber

答えて

1

Widget::all();を呼び出すと、データベース内のすべてのウィジェットレコードが1つずつ取得され、Widgetインスタンスが作成されます。したがって、$widgetsは、Widgetオブジェクトのうち、Collectionのデータベースに格納されます。 10000のウィジェットレコードがある場合は、Collectionが10000 Widgetのオブジェクトになります。これは明らかにあなたが望むものではありません。

$widgets->where()...を呼び出すと、SQLを使用してデータベース結果をフィルタリングする代わりに、PHPを使用してオブジェクトのコレクションをフィルタリングしているCollectionオブジェクトのwhere()を呼び出すことになります。

あなたができることはいくつかあります。

最初に、$widgetsDailyのリストにあるIDを持つウィジェットだけが気になることがわかります。したがって、あなたのWidgetクエリは、idのリストにwidget_idを持つレコードのみを含めるように制限してください。

第2に、日付検索をデータベースクエリにも追加します。

第3に、widget_idフィールドで結果のコレクションをキー入力します。毎回コレクションをループしなくても、widget_idでアイテムに直接アクセスできます。

protected function saveWidgetsToDatabase($widgetsDaily, Boost $boost, $date) 
{ 
    // Get the only widget_ids we care about (assumes $widgetsDaily is a collection) 
    $ids = $widgetsDaily->pluck('id')->all(); 

    // Get the target widgets from the database. This collection will only 
    // contain widgets that we actually care about. 
    $widgets = Widget::whereIn('widget_id', $ids) 
     ->where('date', $date) 
     ->get() 
     ->keyBy('widget_id'); // rekey the resulting collection 

    foreach ($widgetsDaily as $widgetDaily) { 
     // Because the collection was rekeyed on widget_id, you can use 
     // get(id) instead of having to use where('widget_id', id)->first() 
     $existingWidget = $widgets->get($widgetDaily->id); 

     if ($existingWidget === null) 
      $boost->widgets()->save(new Widget([ 
       ... 
      ])); 
     else 
      $existingWidget->update([ 
       ... 
      ]); 
    } 
}