Sur*_*raj 13 php database cursor large-data laravel
我想知道laravel chunk和laravel cursor方法之间有什么区别.哪种方法更适合使用?两者的用例是什么?我知道你应该使用游标来节省内存,但它在后端实际上是如何工作的?
通过示例的详细解释将是有用的,因为我已经搜索了stackoverflow和其他站点,但我没有找到太多信息.
以下是laravel文档中的代码片段.
分块结果
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
Run Code Online (Sandbox Code Playgroud)
使用游标
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
//
}
Run Code Online (Sandbox Code Playgroud)
劉恒溫*_*劉恒溫 27
Cursor()
PDOStatement::fetch()优点
缺点
Chunk()
PDOStatement::fetchAll优点
缺点
TL; 博士
我曾经认为cursor()每次都会做查询并且只在内存中保留一行结果。所以当我看到@mohammad-asghari 的比较表时,我真的很困惑。它一定是幕后的一些缓冲区。
通过跟踪 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;
}
}
Run Code Online (Sandbox Code Playgroud)
我了解 Laravel 通过包装PDOStatement::fetch()来构建此功能。通过搜索缓冲区 PDO fetch和MySQL,我找到了这个文档。
https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php
查询默认使用缓冲模式。这意味着查询结果会立即从 MySQL 服务器传输到 PHP,然后保存在 PHP 进程的内存中。
因此,通过执行 PDOStatement::execute() 我们实际上以一个为单位获取整个结果行并存储在内存中,而不仅仅是一行。所以如果结果太大,就会导致内存不足异常。
虽然显示的文档我们可以$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);用来摆脱缓冲查询。但缺点应该是谨慎。
无缓冲的 MySQL 查询执行查询,然后在数据仍在 MySQL 服务器上等待获取时返回资源。这在 PHP 端使用较少的内存,但会增加服务器的负载。除非从服务器获取完整的结果集,否则无法通过同一连接发送进一步的查询。无缓冲查询也可以称为“使用结果”。
Olu*_*kin 16
确实这个问题可能会吸引一些自以为是的答案,但简单的答案就在Laravel Docs中
仅供参考:
这是块:
这是光标:
Chunk从数据库中检索记录,并将其加载到内存中,同时将光标设置在检索到的最后一条记录上,这样就不会发生冲突.
因此,这里的优点是,如果您希望在发送大记录之前重新格式化大记录,或者您希望每次对第n个记录执行操作,那么这很有用.例如,如果您正在构建一个视图输出/ excel表,那么您可以记录计数直到它们完成,以便所有这些记录不会立即加载到内存中,从而达到内存限制.
Cursor使用PHP生成器,你可以检查php生成器页面,但这里有一个有趣的标题:
虽然我无法保证我完全理解Cursor的概念,但对于Chunk,chunk在每个记录大小运行查询,检索它,并将其传递到闭包中以进一步处理记录.
希望这很有用.
moh*_*ari 13
我们有一个比较: 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 |
+-------------+-----------+------------+
Run Code Online (Sandbox Code Playgroud)
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 |
+--------------+------------+------------+
Run Code Online (Sandbox Code Playgroud)
小智 9
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;
...
Run Code Online (Sandbox Code Playgroud)
cursor基于PDOStatement::fetch和Generator.
$cursor = DB::table('users')->select('*')->cursor()
foreach ($cursor as $e) { }
Run Code Online (Sandbox Code Playgroud)
会发出一个查询:
select * from `users`
Run Code Online (Sandbox Code Playgroud)
但是驱动程序不会立即获取结果集.