laravelチャンクとlaravel=カーソルメソッド。どのメソッドが使用に適していますか?使用例は何ですか?カーソルを使用してメモリを節約する必要があることを知っていますが、バックエンドで実際にどのように機能するのですか?
私はstackoverflowや他のサイトで検索しましたが、多くの情報が見つかりませんでしたので、例で詳細な説明が役立つでしょう。
laravelドキュメントからのコードスニペットです。
チャンキング結果
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
カーソルを使用
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
//
}
確かに、この質問はいくつかの意見のある答えを引き付けるかもしれませんが、簡単な答えはここにあります Laravel Docs
参考のために:
これはチャンクです:
これはカーソルです:
チャンクはデータベースからレコードを取得し、メモリにロードしますが、最後に取得したレコードにカーソルを設定して、クラッシュが発生しないようにします。
したがって、ここでの利点は、送信する前にlargeレコードを再フォーマットする場合、またはn番目のレコードで操作を実行する場合です。時間ごとに、これは便利です。たとえば、ビューアウト/ Excelシートを作成している場合、すべてのレコードが一度にメモリにロードされずにメモリ制限に達することがないように、完了するまでレコードをカウントできます。
カーソルはPHP Generatorsを使用します。 php generators ページを確認できますが、興味深いキャプションがあります。
Cursorの概念を完全に理解していることを保証することはできませんが、Chunkの場合、chunkはレコードサイズごとにクエリを実行し、それを取得して、レコードをさらに処理するためにクロージャに渡します。
これが役立つことを願っています。
比較があります:chunk()vs cursor()
10,000レコード:
+-------------+-----------+------------+
| | Time(sec) | Memory(MB) |
+-------------+-----------+------------+
| get() | 0.17 | 22 |
| chunk(100) | 0.38 | 10 |
| chunk(1000) | 0.17 | 12 |
| cursor() | 0.16 | 14 |
+-------------+-----------+------------+
100,000レコード:
+--------------+------------+------------+
| | Time(sec) | Memory(MB) |
+--------------+------------+------------+
| get() | 0.8 | 132 |
| chunk(100) | 19.9 | 10 |
| chunk(1000) | 2.3 | 12 |
| chunk(10000) | 1.1 | 34 |
| cursor() | 0.5 | 45 |
+--------------+------------+------------+
chunk
はページネーションに基づいており、ページ番号を維持し、ループを実行します。
たとえば、DB::table('users')->select('*')->chunk(100, function($e) {})
は、結果セットがチャンクサイズ(100
)より小さくなるまで複数のクエリを実行します。
select * from `users` limit 100 offset 0;
select * from `users` limit 100 offset 100;
select * from `users` limit 100 offset 200;
select * from `users` limit 100 offset 300;
select * from `users` limit 100 offset 400;
...
cursor
は、PDOStatement::fetch
およびGeneratorに基づいています。
$cursor = DB::table('users')->select('*')->cursor()
foreach ($cursor as $e) { }
単一のクエリを発行します:
select * from `users`
ただし、ドライバーは結果セットを一度に取得しません。
Cursor()
PDOStatement::fetch()
を呼び出して結果を取得します長所
短所
Chunk()
PDOStatement::fetchAll
_を呼び出して結果を取得します長所
短所
TL; DR
cursor()は毎回クエリを実行し、1行の結果のみをメモリに保持すると考えていました。 @ mohammad-asghariの比較表を見たとき、本当に混乱しました。背後にあるbufferでなければなりません。
Laravel以下のコードで追跡
_/**
* Run a select statement against the database and returns a generator.
*
* @param string $query
* @param array $bindings
* @param bool $useReadPdo
* @return \Generator
*/
public function cursor($query, $bindings = [], $useReadPdo = true)
{
$statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
if ($this->pretending()) {
return [];
}
// First we will create a statement for the query. Then, we will set the fetch
// mode and prepare the bindings for the query. Once that's done we will be
// ready to execute the query against the database and return the cursor.
$statement = $this->prepared($this->getPdoForSelect($useReadPdo)
->prepare($query));
$this->bindValues(
$statement, $this->prepareBindings($bindings)
);
// Next, we'll execute the query against the database and return the statement
// so we can return the cursor. The cursor will use a PHP generator to give
// back one row at a time without using a bunch of memory to render them.
$statement->execute();
return $statement;
});
while ($record = $statement->fetch()) {
yield $record;
}
}
_
Laravel wrapPDOStatement :: fetch()でこの機能を構築します。そして、検索buffer PDO fetchおよびMySQL、このドキュメントを見つけました。
https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php
クエリはデフォルトでバッファモードを使用しています。これは、クエリ結果がMySQLサーバーからPHPに直ちに転送され、その後PHPプロセスのメモリに保持されることを意味します。
pDOStatement :: execute()を実行することで、実際に結果行全体を1で取得し、メモリに格納します、1行だけではありません。したがって、結果が非常に大きい場合、これはメモリ不足例外につながります。
示されているドキュメントでは、$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
を使用してバッファリングされたクエリを削除できます。ただし、欠点は注意が必要です。
バッファリングされていないMySQLクエリはクエリを実行し、データがMySQLサーバーでフェッチされるのを待機している間にリソースを返します。これにより、PHP側のメモリ使用量は少なくなりますが、サーバーの負荷が増加する可能性があります。サーバーから完全な結果セットがフェッチされない限り、同じ接続を介してさらにクエリを送信することはできません。バッファリングされていないクエリは、「結果を使用する」とも呼ばれます。