私のアプリケーションには、ユーザーが要求するたびに処理する必要がある大量のデータがあります。スクリプトはもともと foreach ループで構成されていましたが、そのために PHP が毎回タイムアウトになりました。Redis キューの使用に移行しましたが、メモリの問題が発生していました。
mmap() failed: [12] Cannot allocate memory
と
PHP Fatal error: Out of memory (allocated 79691776) (tried to allocate 134217728 bytes)
これで、プロセスが 1 つだけになるようにキューを設定しました。うまく機能しますが、しばらくするとメモリエラーが再び発生し始めます。そして、これは私がテストしているだけです。ユーザーが使い始めると、すぐに倒れてしまいます。
スクリプトを 1024MB 割り当てます。1 回しか使用しないとメモリが不足するからです。スクリプトを実行してメモリを解放するたびに何かできることはないかと考えています。変数の設定解除が好きですか?ただし、スクリプトが終了し、キュー ワーカーによって最初から再実行されるので、これがどのように役立つかはわかりません。
2GB RAM の vagrant マシン (Homestead) を使用しています
アップデート:
ディスパッチャを実行するとバックテストが開始され、10 リーグと 10 年にわたって実行されます。
ディスパッチャ クラス:
class Dispatcher
{
use Attributes;
use DataRetriever;
public function runBacktest($token)
{
$authUserId = Auth::user()->id;
$system = System::where('token', $token)->first();
$this->getSystem( $authUserId, $system->id);
foreach ($this->leagues as $league) {
foreach ($this->years as $year) {
BacktestJob::dispatch($authUserId, $system->id, $token, $league, $year);
}
}
}
}
ディスパッチャーはジョブを実行します。
class BacktestJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $token;
private $league;
private $year;
private $authUserId;
private $systemId;
public $timeout = 10000;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($authUserId, $systemId, $token, $league, $year)
{
$this->token = $token;
$this->league = $league;
$this->year = $year;
$this->authUserId = $authUserId;
$this->systemId = $systemId;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$backtest = new Backtest;
$backtest->init($this->authUserId, $this->systemId, $this->token, $this->league, $this->year);
}
}
以下は、かなり多くのことを行うため、メイン スクリプトの簡略化されたバージョンです。
public function init($authUserId, $systemId, $token, $league, $year)
{
// ini_set('memory_limit', -1);
ini_set('max_execution_time', 300); //300 seconds = 5 minutes
ini_set('memory_limit', '1024M'); // or you could use 1G
$this->authUserId = $authUserId;
$this->systemId = $systemId;
$this->token = $token;
include(storage_path("app/matches/{$league->key}/{$year}.php"));
$this->leagueResults[$league->key][$year] = collect($this->leagueResults[$league->key][$year]);
//Loops through the data - saves to new array
fwrite($file, serialize($backtest));
当初、データは 50MB の json ファイルから取得されました。json ファイルをハードコードされた PHP 配列 (ファイル サイズ 100MB) に置き換えました。新しいファイルの方が大きいことは知っていますが、json_decodeを実行しないと速度が上がると思いました。
スクリプトの最後にある db 挿入も削除しましたが、作業が楽になるのでそのままにしておくことをお勧めします。