【问题标题】:Laravel database optimizationLaravel 数据库优化
【发布时间】:2017-10-23 18:25:23
【问题描述】:

我有一个从外部 API 获取数据并随后将其提交到数据库的代码:

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

我的关系是一个Boost 有很多Widgets。现在,我面临的问题是瓶颈数据库保存/更新,因为只有当它具有相同的日期和 ID 时我才需要更新一个小部件,否则我需要创建一个新的。

我们谈论的是几千条记录,所以我相信where 子句非常密集。

我想进行批量保存,但我没有成功。

有没有机会让它更快?

【问题讨论】:

  • 您是否将widget_iddate 编入索引?
  • 不,不是真的……这有帮助吗?
  • 是的。索引有助于数据库更快地查找信息,并会加快您的where 查询。
  • 请提供生成的SQL。

标签: php mysql performance laravel


【解决方案1】:

当您调用Widget::all(); 时,它会获取数据库中的每条小部件记录并为其创建一个Widget 实例。因此,$widgets 将是存储在数据库中的每个 Widget 对象的 Collection。如果您有 10000 条小部件记录,您将拥有 10000 个 Widget 对象的 Collection。这显然不是你想要的。

这也意味着当您调用$widgets->where()... 时,您是在Collection 对象上调用where(),它使用PHP 过滤对象集合,而不是使用SQL 过滤数据库结果。

您可以做几件事。

首先,您知道您只关心那些在$widgetsDaily 列表中具有 id 的小部件。因此,请将您的 Widget 查询限制为仅包含那些在该 ID 列表中具有 widget_id 的记录。

其次,将日期查找也添加到数据库查询中。

第三,通过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([
                ...
            ]);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-14
    • 2012-07-20
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 2011-11-30
    相关资源
    最近更新 更多