我使用PHP 5.3.6与PDO访问Postgres 9.0.4。我被要求减少报告的内存占用。当前的实现很简单:执行查询,执行fetchAll(),然后通过结果数组迭代foreach()。这显然不能扩展到巨大的结果集:它可以暂时消耗100MB或更多。
我有一个新的实现,它接受PDO语句句柄,然后使用foreach()直接迭代它,即通过fetchAll()没有中间数组。(据我所知,用foreach迭代语句句柄会在后台调用fetch()。)这同样快,并且消耗少得多的内存:大约28kB。尽管如此,我还是不相信我做得对,因为,尽管我已经做了大量的谷歌搜索,很难找到关于这个基本问题的答案:
-
我看到一些文章建议使用光标来解决我最初的问题。Postgress PDO驱动程序已经在内部使用游标了吗?如果需要编写自己的SQL来创建游标,我愿意这样做,但我更愿意编写尽可能简单的代码(但不能更简单!)。
-
如果foreach调用fetch()每次迭代,是不是太网络聊天?或者它是聪明的,一次取出许多行,例如500,以节省带宽?(这可能意味着它在内部使用游标。)
-
我看过一篇文章,它将语句句柄包装在实现Iterator接口的类中。考虑到PDO语句句柄已经这样做了,这不是多余的吗?还是我错过了什么?
-
我准备SQL语句的调用看起来像这样:
我发现这样做对内存和速度没有影响:
$sth = $dbh->prepare($sql, array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );
这是因为这是Postgres PDO驱动程序的默认设置吗?如果它已经在内部使用游标,这将是有意义的。
欢迎对解决这个问题的方法和其他方法提出一般性意见。
PDO for Postgres内部使用游标
显然PDO::CURSOR_FWDONLY
不使用游标。黑盒测试:
(0)准备:
$con = new 'PDO('dsn');
// you'll get "NO ACTIVE TRANSACTION" otherwise
$con->beginTransaction();
$sql = 'select * from largetable';
(1)默认值:
$stmt = $con->prepare($sql);
$stmt->execute();
print_r($stmt->fetch());
(2) FWDONLY - takes forever:
$stmt = $con->prepare($sql, array('PDO::ATTR_CURSOR => 'PDO::CURSOR_FWDONLY));
$stmt->execute();
print_r($stmt->fetch());
(3) SCROLLABLE -在flash中运行:
$stmt = $con->prepare($sql, array('PDO::ATTR_CURSOR => 'PDO::CURSOR_SCROLL));
$stmt->execute();
print_r($stmt->fetch());
我打开PG日志记录只是为了确定,它确实如此-只有SCROLL使用光标。
因此,使用游标的唯一方法是使用SCROLL,至少在PHP 5.4.23中是这样。