【问题标题】:Postgres - run a query in batches?Postgres - 批量运行查询?
【发布时间】:2011-02-01 16:25:21
【问题描述】:

是否可以循环查询,如果(例如)找到 500,000 行,它将返回前 10,000 行的结果,然后再次重新运行查询?

所以,我要做的是运行一个查询并构建一个数组,如下所示:

$result = pg_query("SELECT * FROM myTable");

$i = 0;
while($row = pg_fetch_array($result) ) {
  $myArray[$i]['id'] = $row['id'];
  $myArray[$i]['name'] = $row['name'];
  $i++;
}

但是,我知道会有几十万行,所以我想分批做 10,000... 1- 9,999 然后 10,000 - 10,999 等等...原因是因为我不断得到这个错误:

Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 3 bytes)

顺便说一句,我不明白 3 个字节是如何耗尽 512M 的……所以,如果我可以更改它,那就太好了,尽管分批执行此操作可能会更好?

【问题讨论】:

  • 您没有足够的内存将所有结果在同一时刻(没有 CURSOR)放入数组中。但是,你想做什么?你想通过填充这个数组来解决什么问题。

标签: php postgresql memory loops


【解决方案1】:

最后 3 个字节是压死骆驼的最后一根稻草。可能是一长串分配中的分配尝试导致失败。

不幸的是,libpq 在将控制权交给应用程序之前会尝试将结果集完全缓存在内存中。这是您在$myArray 中使用的任何内存的补充。

建议使用LIMIT ... OFFSET ...来减少内存包络;这会起作用,但效率低下,因为它可能在每次使用不同的偏移量重新发出查询时不必要地重复服务器端排序工作(例如,为了回答 LIMIT 10 OFFSET 10000 ,Postgres 仍然需要对整个结果集进行排序,只返回第 10000..10010 行。)

改为使用DECLARE ... CURSOR 创建一个服务器端游标,然后使用FETCH FORWARD x 获取下一个x 行。根据需要重复多次或直到返回小于x 行。完成后不要忘记CLOSE 光标,即使/如果出现异常。

另外,不要SELECT *;如果您只需要idname,请创建光标FOR SELECT id, name(否则libpq 将不必要地检索和缓存您从未使用过的列,从而增加内存占用和总体查询时间。)

使用如上所示的游标,libpq 将在任何时候在内存中最多保存x 行。但是,如果可能,请确保您还清理了 FETCHes 之间的 $myArray,否则您仍然可能由于 $myArray 而耗尽内存。

【讨论】:

  • LIMIT...OFFSET... 真的涉及排序吗?
  • 如果您希望LIMIT ... OFFSET ... 具有确定性,那么它们将伴随着ORDER BY,除非您有幸需要简单的索引扫描,否则涉及排序。即使你很幸运并且成功完成了索引扫描,它们也是重复的扫描,你可以不用游标来完成。
【解决方案2】:

您可以使用LIMIT (x)OFFSET (y)

【讨论】:

  • 我对此进行了研究,并认为我可以计算总数,然后进行 for 循环,但到目前为止,它似乎不起作用......
  • @Caffeine,您完全不需要需要知道总行数,也不需要for 循环。在while 循环中执行语句,并在查询返回的行数少于您请求的x 行时立即中断循环,这意味着您已经到达终点。
  • @Caffeine,您是否了解使用 LIMIT/OFFSET 而不是光标的负面影响?
【解决方案3】:

PostgreSQL 服务器会缓存查询结果,直到您实际检索它们,因此像这样在循环中将它们添加到数组中无论如何都会导致内存耗尽。要么一次处理一行结果,要么检查数组的长度,处理到目前为止提取的结果,然后清除数组。

【讨论】:

  • 我对此有点困惑......这不是我在做什么吗?运行查询然后处理结果?对不起...我没有关注...
  • 很难理解如何将行添加到数组中视为处理它们。
【解决方案4】:

错误的意思是 PHP 试图分配 3 个字节,但 512MB 的所有可用部分都小于 3 个字节。

即使您分批执行此操作,根据结果数组的大小,您仍然可能会耗尽可用内存。

也许您真的不需要获取所有记录?

【讨论】:

    猜你喜欢
    • 2012-01-04
    • 1970-01-01
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多