简介:php通过游标获取数据,解决PHP读取大量数据时内存不足的bug
在开发导出功能时我们经常会遇到内存超限的问题,那么怎么去解决这个问题?
要解决这个问题首先我们要明白引起问题的原因。我们通过php代码读取数据采用的是缓冲查询方式,这种方式会将数据全部读取出来并放在内存中。
如果你有10万条数据,执行查询SQL后就会将100万的数据全部读取到内存中。这种方式给PHP程序增加了额外的功能,比如说,计算行数,将指针指向某一行等。更重要的是PHP程序可以对数据集反复的进行二次查询和过滤等操作。这种模式最大的缺陷就是占用内存太多。
相对缓存模式,非缓存查询模式可以很好的控制内存的使用。非缓存查询模式PHP会一条一条的从数据库中读取数据,PHP再一条一条的处理数据,处理完数据立刻释放内存。这种方式最大的缺点就是增加数据库的压力,因为数据库会一直等待PHP来读取数据,一直到数据全部取完为止。
从上面的介绍可以看出,缓存模式适合少量数据查询,而非缓存模式适合大量数据的查询。
接下来我们来看看非缓存模式具体的实现代码吧
mysqli扩展实现方式:
<?php
$mysqli = new \mysqli("数据库IP地址", "数据用户名", "数据库密码", "数据库名");
$result= $mysqli->query("SELECT * FROM `order`", MYSQLI_USE_RESULT);
if ($result) {
while ($row = $result->fetch_assoc()) {
echo $row['name'] . PHP_EOL;
}
}
$result->close();
PDO扩展实现方式:
<?php
$pdo = new \PDO("数据库服务器DSN信息", "数据库用户名","数据库密码");
//设置非缓存模型
$pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$result = $pdo->query("SELECT * FROM `orders`");
if ($result) {
while ($row = $result->fetch(\PDO::FETCH_ASSOC,\PDO::FETCH_ORI_NEXT)){
//PHP处理数据逻辑
}
}
MySQL扩展实现方式:
<?php
//链接MySQL
$connect = mysql_connect("数据库服务器地址", '数据库用户名', '数据库密码');
//选择数据库
mysql_select_db("数据库名",$connect);
$result = mysql_unbuffered_query("SELECT * FROM `cupboard_shopping_order`");
if ($result) {
while ($row = mysql_fetch_assoc($result)) {
//PHP对数据进行处理逻辑
}
}
在实际生成环境中,我们可能只安装了pdo或者mysqli扩展,因此我们需要判断当前环境支持哪种模式。大家可以使用extension_loaded 函数判断是否支持某个扩展
<?php
//如果已安装pdo返回true
extension_loaded('pdo');
//如果已安装mysqli返回true
extension_loaded('mysqli');
//如果已安装mysql返回true
extension_loaded('mysql');
如果你使用了市面上的一些框架,框架中提供了类似批量处理数据的方法。很不幸的告诉大家这些批量处理方法依然是缓存模式,依然会报内存超限的错误,具体错误如下所示:
PHP Fatal error: Allowed memory size of 268 435 456 bytes exhausted
经我个人测试tp及yii框架提供的批量处理及指针的方式都有问题,无法像上面的代码一下一条一条处理数据。大家还是使用上面的原生php代码进行大量数据操作吧
上述内容有什么遗漏或者不对的地方,请在公众号留言