我可以优化这个脚本更新 ~6000 行的大量数据吗
Can I optimize this script updating ~6000 rows with a lot of data
我有 ~5-6k $items
需要在数据库中更新。每个项目都需要一个 HTTP 请求来从页面获取数据。在 HTTP GET 请求中,我得到了大量的数组 (~500-2500),我只需要插入那些不在数据库中的行。在我的 vagrant scotch box 上使用我当前的脚本(每 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
.
我可以提供一个简化的答案 - 同步执行、对象水合和批量数据库查询。
考虑以下示例:
$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.
}
);
}
同步 Http 查询 - 上面的示例突出显示了 Guzzle 的一些异步功能,同时分解了处理步骤。您上面链接的代码是同步的。执行请求、等待响应、处理响应、重复并重复。异步 Http 请求将确保在处理其他信息的同时下载数据。请注意,您的结果会有所不同,并且根据您的特定用例,资源使用量可能会增加。
对象水化——也就是当你执行查询时你的 ORM 正在做什么,它 returns 一个对象实例(而不是一个数组)是耗时和内存密集型的。 @orcamius(Doctrine 的开发者之一)在 subject 上写了一篇相当技术性的文章。虽然这不是 Eloquent 特定的,但它确实提供了对所有 ORM 的幕后操作的洞察力。代码片段执行其中的许多操作(参考 $itemHistory
、$history
、Item::where
)。
批量数据库操作- 一个众所周知的事实是数据库操作很慢。当与物体水合作用时,这个时间会进一步增加。执行具有 1000 条记录的单个插入与 1000 条插入要好得多。为此,必须将代码从使用 ORM 修改为直接使用数据库表。如 docs
中所示,可以通过 DB::table('itemHistory')->insert($arrayOfValues)
执行批量插入
更新:虽然未显示 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 Promise 的更多信息
我有 ~5-6k $items
需要在数据库中更新。每个项目都需要一个 HTTP 请求来从页面获取数据。在 HTTP GET 请求中,我得到了大量的数组 (~500-2500),我只需要插入那些不在数据库中的行。在我的 vagrant scotch box 上使用我当前的脚本(每 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
.
我可以提供一个简化的答案 - 同步执行、对象水合和批量数据库查询。
考虑以下示例:
$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.
}
);
}
同步 Http 查询 - 上面的示例突出显示了 Guzzle 的一些异步功能,同时分解了处理步骤。您上面链接的代码是同步的。执行请求、等待响应、处理响应、重复并重复。异步 Http 请求将确保在处理其他信息的同时下载数据。请注意,您的结果会有所不同,并且根据您的特定用例,资源使用量可能会增加。
对象水化——也就是当你执行查询时你的 ORM 正在做什么,它 returns 一个对象实例(而不是一个数组)是耗时和内存密集型的。 @orcamius(Doctrine 的开发者之一)在 subject 上写了一篇相当技术性的文章。虽然这不是 Eloquent 特定的,但它确实提供了对所有 ORM 的幕后操作的洞察力。代码片段执行其中的许多操作(参考
$itemHistory
、$history
、Item::where
)。批量数据库操作- 一个众所周知的事实是数据库操作很慢。当与物体水合作用时,这个时间会进一步增加。执行具有 1000 条记录的单个插入与 1000 条插入要好得多。为此,必须将代码从使用 ORM 修改为直接使用数据库表。如 docs
中所示,可以通过
DB::table('itemHistory')->insert($arrayOfValues)
执行批量插入
更新:虽然未显示 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 Promise 的更多信息