PHP处理大数据量循环记录的高效解决方案与内存优化技巧
[id_[id_972043423]09618341]

当我们需用 php 处理大数据量时,像循环数据库的所有记录这种情况,此记录可能会很大,像有 100 万行。那么,通过传统办法就不行了。首先是通过 web 访问,php 有超时时间,默认是 30 秒。只能通过 php cli 的方式来执行,这样就不存在超时时间了。然而,因为数据量很大,在 php 循环时需要将全部数据载入内存中执行,正因为数据量太大,所以 php 会报出“Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes)”这样的错误。PHP 通过生成器函数能够极大地节省内存开销。
生成器函数
生成器函数的外观与普通函数相似。普通函数会返回一个值,而生成器能够通过 yield 产出多个想要的值。只要一个函数中包含 yield,那么它就是一个生成器函数。
当调用一个生成器时,它会返回一个可遍历的对象。当对这个对象进行遍历(比如通过 foreach 循环),PHP 会在每次需要值的时候调用对象的遍历方法,并且在产生一个值之后保存生成器的状态,以便在需要产生下一个值时能恢复调用状态。
yield关键字
生成器函数的核心是 yield 关键字。yield 关键字与 return 申明有相似之处。普通 return 会返回值并终止函数执行,而 yield 会返回一个值给循环调用此生成器的代码,并且只是暂停执行生成器函数。
一个简单的例子
以上例子会输出:
1
2
3
遍历数据库实例
当前我们需要对数据库的 emp 表进行循环。该表拥有 100 万行记录。我们要循环这个表并修改 mgr 的字段值。
未使用生成器
$db = sf::getLib('db');
从“emp”表中选择所有数据,条件是“mgr = 1”,然后按照“id”升序进行排序。
$query = $db->query($sql);
当 ($row = $db->fetch_array($query)) 时:
$db 执行一条更新语句,将 emp 表中 id 为 '{$row['id']}' 的记录的 mgr 字段更新为 2 。
输出“id:”以及变量 $row['id'] 的值,并换行
}
当数据量较小时,这不会产生问题。然而,倘若数据量较大,就无法执行,并且会出现内存超过限制的错误。
使用生成器
function test_yield(){
$db = sf::getLib('db');
从“emp”表中选择所有内容,条件是“mgr = 1”,并且按照“id”升序进行排序。
$query = $db->query($sql);
当 $row 等于 $db 调用 fetch_array 方法返回的结果时,进行以下操作;在每次循环中,$row 会被更新为 $db 执行 $query 语句后返回的下一行数据;只要 $row 不为空,就会持续执行循环体中的代码;当 $row 为空时,循环结束。
yield $row;
}
}
对于每个由 test_yield() 生成的项($item):
$db->exec("update emp set mgr = 2 where id = '{$row['id']}'");
输出“id:”与$item['id']拼接后的内容,并换行
}
程序借助 test_yield 这个生成器函数能够正常执行,同时内存也不会超限。