【问题标题】:Can I optimize this script updating ~6000 rows with a lot of data我可以用大量数据优化这个脚本更新 ~6000 行吗
【发布时间】:2016-03-17 01:25:24
【问题描述】:

我有 ~5-6k $items 需要在数据库中更新。每个项目都需要一个 HTTP 请求来从页面获取数据。在 HTTP GET 请求中,我得到了很大的数组(~500-2500),我只需要插入那些不在数据库中的行。我的流浪苏格兰威士忌盒上的当前脚本(每 2-4 分钟 1 个项目)似乎需要很长时间。

简化示例:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use GuzzleHttp\Client;
use App\Item;
use App\ItemHistory;
use Carbon\Carbon;

use DB;

class UpdateController extends Controller
{
    public function getStart() {
        // Don't cancel the script
        ignore_user_abort(true);
        set_time_limit(0);

        $client = new Client();
        $items = Item::where('updated_at', '<=', Carbon::now()->subDay())->get();

        foreach($items as $item) {
            $response = $client->request('GET', 'API_URL');
            // get the body
            $body = $response->getBody()->getContents();

            $hugeArray = $body['history']; // can be from 100 to 5 000 lines and I use regex to get the "history" array from the body
            $arrayCollection = collect($hugeArray);

            foreach($arrayCollection->take(-100) as $row) { // I take the last 100 since each row = 1 hour, so I get items in the last 100 hours
                $date = new \DateTime($row['created_at']);
                if( ! ItemHistory::whereItemId($item->id)->whereSoldAt($date)->count()) { // Checking if it already exists
                    // I insert the new rows..
                    $history = new ItemHistory;
                    // ....
                    $history->save();
                }
            }
        }
    }
}

我实际上是抓取数据并使用正则表达式在正文响应中查找数组。 难道我做错了什么?它需要很长时间才能移动到下一个$item

【问题讨论】:

标签: laravel eloquent guzzle


【解决方案1】:

我可以提供一个简化的答案 - 同步执行、对象混合和批量数据库查询。

考虑以下示例:

$requests = function () use ($items) {
    foreach ($items as $item) {
        yield new GuzzleHttp\Psr7\Request($method, $uri);
    }
};

$client = new GuzzleHttp\Client();

foreach ($requests() as $request) {
    $client->sendAsync($request)
        ->then(
            function(Psr7\Http\Message\ResponseInterface) {
                // process the response into array;

                return $arrayFromResponse;
        })
        ->then(
            function ($unfilteredArray) {
                // filter the array as necessary

                return $filteredArray;
        })
        ->then(
            function($filteredArray) { 
                // create the array for bulk insert / update

                return $sqlArray;
        })
        ->then(
            function($sqlArray) {
                // perform bulk db operations.
            }
        );
}
  1. 同步 Http 查询 - 上面的示例突出了 Guzzle 的一些异步功能,同时打破了处理步骤。您上面链接的代码是同步的。执行请求,等待响应,处理响应,执行并重复。异步 Http 请求将确保在处理其他信息的同时下载数据。我应该注意,您的结果会有所不同,并且根据您的特定用例,可能会看到资源使用量增加。

  2. 对象水合 - 也就是当您执行查询并返回对象实例(而不是数组)时您的 ORM 正在执行的操作,既耗时又占用内存。 @orcamius(Doctrine 的开发者之一)在subject 上写了一篇相当技术性的文章。虽然这不是 Eloquent 特有的,但它确实提供了对所有 ORM 的幕后操作的洞察。代码 sn-p 执行其中的许多操作(参考 $itemHistory$historyItem::where)。

  3. 批量数据库操作 - 众所周知的事实是数据库操作很慢。当与物体水合作用时,这个时间会进一步增加。执行 1000x 记录的单次插入与 1000x 插入相比要好得多。为此,必须将代码从使用 ORM 修改为直接使用 DB 表。批量插入可由DB::table('itemHistory')-&gt;insert($arrayOfValues) 执行,如docs中所示

更新:虽然没有显示 then() 具有 then(callable $fulfilled, callable $onError) 的方法签名。如果请求出现问题,您可以执行类似的操作

// promise returned from a request
$p->then(
    function (Psr\Http\Message\ResponseInterface $response) use ($p)
        if ($response->getResponseCode() >= 400) {
            $p->cancel();
        }
        //perform processing
        return $someArray;
    },
    function (RequestException $e) {
        echo $e->getMessage() . "\n";
        echo $e->getRequest()->getMethod();
    })
->then(
    function($someArray) use ($p) {
        // filter or other processing
    });

可以在Github Repo 中找到有关 Guzzle 承诺的更多信息

【讨论】:

  • 假设请求失败。要么我得到一个不是 200 的 HTTP 代码,要么我得到一个没有正确数组结构的响应。我怎样才能打破“当时”的承诺链?
  • 更新回复以解决您的问题。
  • Foreach $requests 不会迭代那些产生的请求。我尝试改用$requests(),现在我得到了500 HTTP。在这种情况下使用new Pool 也可以吗?
  • 有一个错字。已更正。您也可以使用池。这是限制您的应用程序正在建立的活动连接数量的好方法。
  • 为简单起见,我省略了可选的“请求选项”数组。您可以在对象构造函数中添加查询,也可以使用接受 Psr\Http\Message\UriInterface 的 withUri() 方法修改请求。供参考:guzzle.readthedocs.org/en/latest/request-options.htmlgithub.com/php-fig/http-message/blob/master/src/…github.com/php-fig/http-message/blob/master/src/…
猜你喜欢
  • 1970-01-01
  • 2017-11-06
  • 1970-01-01
  • 2011-04-25
  • 1970-01-01
  • 2014-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多