PHP大量数据循环时内存耗尽问题的解决方案

本文介绍了在PHP开发中如何使用非缓冲查询解决大数据量查询时内存溢出的问题,并提供了三种实现非缓冲查询的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在开发一个PHP程序时遇到了下面的错误:

PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted

错误信息显示允许的最大内存已经耗尽。遇到这样的错误起初让我很诧异,但转眼一想,也不奇怪,因为我正在开发的这个程序是要用一个foreach循环语句在一个有4万条记录的表里全表搜索具有特定特征的数据,也就是说,一次要把4万条数据取出,然后逐条检查每天数据。可想而知,4万条数据全部加载到内存中,内存不爆才怪。

毕竟编程这么多年,我隐约记得PHP里提供有非一次全部加载数据的API,是像处理流媒体那样,随用随取随丢、数据并不会积累在内存的查询方法。经过简单的搜索,果然在官方网站上找到的正确的用法。

这个问题在PHP的官方网站上叫缓冲查询和非缓冲查询(Buffered and Unbuffered queries)。   PHP的查询缺省模式是缓冲模式。也就是说,查询数据结果会一次全部提取到内存里供PHP程序处理。这样给了PHP程序额外的功能,比如说,计算行数,将  指针指向某一行等。更重要的是程序可以对数据集反复进行二次查询和过滤等操作。但这种缓冲查询模式的缺陷就是消耗内存,也就是用空间换速度。

相对的,另外一种PHP查询模式是非缓冲查询,数据库服务器会一条一条的返回数据,而不是一次全部返回,这样的结果就是PHP程序消耗较少的内存,但却增加了数据库服务器的压力,因为数据库会一直等待PHP来取数据,一直到数据全部取完。

很显然,缓冲查询模式适用于小数据量查询,而非缓冲查询适应于大数据量查询。

对于PHP的缓冲模式查询大家都知道,下面列举的例子是如何执行非缓冲查询API。

非缓冲查询方法一: mysqli

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

if ($uresult) {
   while ($row = $uresult->fetch_assoc()) {
       echo $row['Name'] . PHP_EOL;
   }
}
$uresult->close();
?>

非缓冲查询方法二: pdo_mysql

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

$uresult = $pdo->query("SELECT Name FROM City");
if ($uresult) {
   while ($row = $uresult->fetch(PDO::FETCH_ASSOC)) {
       echo $row['Name'] . PHP_EOL;
   }
}
?>

非缓冲查询方法三: mysql

<?php
$conn = mysql_connect("localhost", "my_user", "my_pass");
$db   = mysql_select_db("world");

$uresult = mysql_unbuffered_query("SELECT Name FROM City");
if ($uresult) {
   while ($row = mysql_fetch_assoc($uresult)) {
       echo $row['Name'] . PHP_EOL;
   }
}
?>


转载于:https://my.oschina.net/snowing1990/blog/660731

### 解决PHP内存耗尽错误问题PHP中,当程序运行占用的内存量超过了`memory_limit`设置的限制,会抛出“Allowed memory size exhausted”的致命错误。这个问题可能出现在多种场景下,例如Laravel、Monolog或Doctrine等框架中。 #### 1. 增加PHP内存限制 可以通过修改`php.ini`文件中的`memory_limit`参数来增加允许的最大内存使用量。例如,将`memory_limit`设置为更高的值: ```ini memory_limit = 512M ``` 如果无法直接修改`php.ini`文件,也可以通过以下方法动态调整内存限制: - 在代码中使用`ini_set`函数: ```php ini_set('memory_limit', '512M'); ``` 此方法仅对当前脚本有效。 - 使用`.htaccess`文件(适用于Apache服务器): ```apache php_value memory_limit 512M ``` #### 2. 捕获内存溢出错误 尽管可以通过增加内存限制来解决大部分问题,但在某些情况下仍需捕获内存溢出错误以避免程序崩溃。可以使用`try-catch`块捕获异常,但需要注意PHP的致命错误通常不会被捕获。为了处理这种情况,可以注册一个自定义错误处理器: ```php set_error_handler(function($errno, $errstr) { if (strpos($errstr, 'Allowed memory size') !== false) { error_log('Memory limit exceeded: ' . $errstr); // 自定义逻辑,例如发送警报邮件或记录日志 } }); ``` 此外,在循环或其他可能导致内存泄漏的操作中,应定期检查内存使用情况: ```php if (memory_get_usage() > 400 * 1024 * 1024) { // 如果内存使用超过400MB throw new \Exception('Memory usage is too high'); } ``` #### 3. Laravel中的内存优化 在Laravel中,内存溢出问题可能与日志记录(Monolog)、数据库操作(Doctrine)或队列任务有关。以下是针对这些场景的解决方案: - **Monolog**:确保日志文件不会无限增长。可以通过配置`logging.php`中的`daily`驱动来限制日志文件的数量和大小: ```php 'default' => env('LOG_CHANNEL', 'stack'), 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['single'], 'days' => 7, // 保留最近7天的日志 ], ], ``` - **Doctrine**:在批量处理数据,关闭持久化上下文以释放内存: ```php $entityManager->clear(); ``` - **队列任务**:对于长间运行的任务,建议将其拆分为多个小任务,并通过队列系统逐步执行。 #### 4. Composer相关问题 如果在运行Composer遇到内存不足的问题,可以指定PHP二进制文件路径并增加内存限制: ```bash /usr/local/php71/bin/php -d memory_limit=512M /usr/local/bin/composer install ``` 或者直接在命令行中增加内存限制: ```bash COMPOSER_MEMORY_LIMIT=-1 composer install ``` #### 5. 其他注意事项 确保服务器有足够的可用内存。如果运行的是虚拟机或容器环境,可能需要调整分配给该环境的资源。 --- ### 示例代码 以下是一个示例,展示如何动态调整内存限制并在循环中检查内存使用情况: ```php <?php ini_set('memory_limit', '256M'); function processLargeData() { $content = ''; while (true) { $content .= str_repeat('a', 1024 * 1024); // 每次增加1MB数据 if (memory_get_usage() > 200 * 1024 * 1024) { // 如果内存使用超过200MB error_log('Memory usage exceeded the limit'); break; } } } processLargeData(); ``` ---这段代码写在那个文件里?
最新发布
06-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值