在 Postgres/PHP/PDO 中迭代结果集的最佳实践?

发布于 2024-12-11 21:52:14 字数 994 浏览 0 评论 0原文

我使用 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);

我发现如果我这样做,它不会产生内存或速度差异:

$sth = $dbh->prepare($sql, array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );

这是因为这是 Postgres PDO 驱动程序的默认设置吗?如果它已经在内部使用游标,这将是有意义的。

欢迎对该方法和其他解决此问题的方法提出一般性意见。

I'm using PHP 5.3.6 with PDO to access Postgres 9.0.4. I've been asked to reduce the memory footprint of a report. The current implementation is simple: execute the query, do a fetchAll() and then iterate with foreach() through the resulting array. This obviously doesn't scale with huge result sets: it can temporarily consume 100MB or more.

I have a new implementation which takes the PDO statement handle and then iterates directly on it using foreach(), i.e. no intermediate array via fetchAll(). (From what I've read, iterating a statement handle with foreach calls fetch() under the covers.) This is just as fast and consumes way less memory: about 28kB. Still, I'm not confident I'm doing it right because, although I've done a ton of Googling, it's tough to find answers to basic questions about this:

  • I've seen articles that suggest solving my original problem using cursors. Does the Postgress PDO driver already use cursors internally? If writing my own SQL to create a cursor is required, I'm willing to but I'd prefer to write the simplest code possible (but no simpler!).

  • If foreach calls fetch() each iteration, isn't that too network chatty? Or is it smart and fetches many rows at once, e.g. 500, to save bandwidth? (This may imply that it uses cursors internally.)

  • I've seen an article that wraps the statement handle in a class that implements Iterator interface. Isn't this redundant given that a PDO statement handle already does this? Or am I missing something?

  • My call to prepare the SQL statement looks like this:

    $sth = $dbh->prepare($sql);

I found that it made no memory or speed difference if I did this:

$sth = $dbh->prepare($sql, array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );

Is this because this is the default anyway for the Postgres PDO driver? This would make sense if it is already using cursors internally.

General comments about the approach and other ways to solve this problem are welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

安静被遗忘 2024-12-18 21:52:14

显然 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 - 永远运行:

$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY));
$stmt->execute();
print_r($stmt->fetch());

(3) 可滚动 - 瞬间运行:

$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL));
$stmt->execute();
print_r($stmt->fetch());

我打开 PG 日志记录只是为了确定,确实如此 -只有 SCROLL 使用游标。

因此,使用游标的唯一方法是使用 SCROLL,至少在 PHP 5.4.23 中是这样。

Apparently PDO::CURSOR_FWDONLY does not use cursors. Black box tests:

(0) Preparations:

$con = new \PDO('dsn');
// you'll get "NO ACTIVE TRANSACTION" otherwise
$con->beginTransaction();

$sql = 'select * from largetable';

(1) Default - takes forever:

$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 - runs in a flash:

$stmt = $con->prepare($sql, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_SCROLL));
$stmt->execute();
print_r($stmt->fetch());

I turned on PG logging just to be sure and it is indeed so - only SCROLL uses cursors.

So, the only way to make use of cursors is to use SCROLL, at least in PHP 5.4.23.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文