Postgres - 批量运行查询?

时间:2010-03-28 05:19:19

标签: php postgresql memory loops

是否可以遍历查询,以便如果(例如)找到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的...所以,如果那是我可以改变的东西,那就太好了,尽管如此,批量生产仍然可能更好?

4 个答案:

答案 0 :(得分:6)

最后3个字节是打破骆驼背部的稻草。可能是在长串分配中导致失败的分配尝试。

不幸的是,libpq会在放弃对应用程序的控制之前尝试在内存中完全缓存结果集。这是您在$myArray中消耗的任何内存的补充。

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

相反,使用DECLARE ... CURSOR创建服务器端游标,然后使用FETCH FORWARD x来获取下一个LIMIT 10 OFFSET 10000行。根据需要重复多次,或直到返回少于x行。完成后不要忘记CLOSE光标,即使/如果异常上升。

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

如上所述使用游标,libpq任何时候都会在内存中保留最多libpq行。但是,请确保尽可能在x之间清除$myArray,否则您仍可能因FETCH而内存不足。

答案 1 :(得分:3)

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

错误意味着PHP正在尝试分配3个字节,但512MB的所有可用部分都少于3个字节。

即使您分批进行,依赖于生成的数组的大小,您仍然可以耗尽可用内存。

也许你真的不需要获得所有记录?