dpf5207 2017-10-23 18:25
浏览 146
已采纳

Laravel数据库优化

I have a code which fetches data from external API and commits it to DB afterwards:

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

Relation I have is that one Boost has many Widgets. Now, the issue I'm facing is bottleneck DB saving/updating as I need to update a widget only if it has same date and ID, otherwise I need to create new one.

We are talking about few thousands of records, so I believe that where clauses are pretty intensive.

I wanted to make a batch save, though I didn't quite make it.

Are there any chances of making this faster?

  • 写回答

1条回答 默认 最新

  • dqdes60666 2017-10-25 00:48
    关注

    When you call Widget::all();, that gets every single widget record in your database and creates a Widget instance for it. Therefore, $widgets will be a Collection of every Widget object stored in the database. If you have 10000 widget records, you'll have a Collection of 10000 Widget objects. This is obviously not what you want.

    That also means that when you call $widgets->where()..., you're calling where() on the Collection object, which is using PHP to filter through the collection of objects, instead of using SQL to filter the database results.

    There are a couple things you can do.

    First, you know you only care about those widgets that have an id in the list of $widgetsDaily. So, limit your Widget query to only include those records that have a widget_id in that list of ids.

    Second, add the date lookup to the database query as well.

    Third, key the resulting collection by the widget_id field, so that you can directly access the item by the widget_id without having to loop through the entire collection looking for it every time.

    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([
                    ...
                ]);
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 易语言把MYSQL数据库中的数据添加至组合框
  • ¥20 求数据集和代码#有偿答复
  • ¥15 关于下拉菜单选项关联的问题
  • ¥20 java-OJ-健康体检
  • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
  • ¥15 使用phpstudy在云服务器上搭建个人网站
  • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
  • ¥15 vue3+express部署到nginx
  • ¥20 搭建pt1000三线制高精度测温电路
  • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况