2024年PHP日本大会

缓冲查询和非缓冲查询

查询默认使用缓冲模式。这意味着查询结果会立即从MySQL服务器传输到PHP,然后保存在PHP进程的内存中。这允许额外的操作,例如计算行数和移动(查找)当前结果指针。它还允许在处理结果集的同时对同一连接发出进一步的查询。缓冲模式的缺点是较大的结果集可能需要相当多的内存。内存将一直占用,直到所有对结果集的引用都被取消或结果集被显式释放,这最迟将在请求结束时自动发生。“存储结果”一词也用于缓冲模式,因为整个结果集一次性存储。

注意:

当使用libmysqlclient作为库时,除非数据被提取到PHP变量中,否则PHP的内存限制不会计算用于结果集的内存。使用mysqlnd时,所占用的内存将包括完整的结果集。

非缓冲MySQL查询执行查询,然后等待从MySQL服务器提取数据。这减少了PHP端的内存使用,但会增加服务器的负载。除非从服务器提取了完整的结果集,否则无法通过同一连接发送进一步的查询。非缓冲查询也可以称为“使用结果”。一旦提取了结果集中的所有行,结果集就消失了,并且无法再次迭代。

根据这些特性,只有在预期会顺序处理大型结果集时才应使用非缓冲查询。非缓冲查询包含许多陷阱,这使得使用它们更加困难,例如,结果集中的行数在提取最后一行之前是未知的。缓冲查询是处理结果集更简单、更灵活的方法。

因为缓冲查询是默认的,所以下面的示例将演示如何使用每个API执行非缓冲查询。

示例 #1 非缓冲查询示例:mysqli

<?php
$mysqli
= new mysqli("localhost", "my_user", "my_password", "world");
$unbufferedResult = $mysqli->query("SELECT Name FROM City", MYSQLI_USE_RESULT);

foreach (
$unbufferedResult as $row) {
echo
$row['Name'] . PHP_EOL;
}
?>

示例 #2 非缓冲查询示例:pdo_mysql

<?php
$pdo
= new PDO("mysql:host=localhost;dbname=world", 'my_user', 'my_password');
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$unbufferedResult = $pdo->query("SELECT Name FROM City");
foreach (
$unbufferedResult as $row) {
echo
$row['Name'] . PHP_EOL;
}
?>
添加注释

用户贡献的注释 1 条注释

polygon dot co dot in at gmail dot com
1年前
缓冲和非缓冲查询可用于有限数量的记录。

例如:在使用缓冲方式实现查询下载CSV时,超过30,000条记录需要缓冲,就会出现内存限制问题。

类似地,对于非缓冲方式,负载会转移到数据库服务器。

可以通过以下方法减少Web服务器(缓冲)和MySQL服务器(非缓冲)上的此负载,从而支持下载30,000+条记录的CSV。

<?php
// shell 命令
$shellCommand = 'mysql '
. '--host='.escapeshellarg($hostname).' '
. '--user='.escapeshellarg($username).' '
. '--password='.escapeshellarg($password).' '
. '--database='.escapeshellarg($database).' '
. '--execute='.escapeshellarg($sql).' '
. '| sed -e \'s/"/""/g ; s/\t/","/g ; s/^/"/g ; s/$/"/g\'';

// CSV 头部
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename={$csvFilename}");
header("Pragma: no-cache");
header("Expires: 0");

// 通过 shell 执行命令并回显完整的输出作为字符串
echo shell_exec($shellCommand);
?>

sed正则表达式会消耗一些CPU。
To Top