Postgres - запустить запрос партиями? - PullRequest
5 голосов
/ 28 марта 2010

Можно ли выполнить цикл запроса так, чтобы, если (например) было найдено 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 ]

6 голосов
/ 28 марта 2010

Эти последние 3 байта были соломой, которая сломала спину верблюду. Вероятно, попытка выделения в длинной строке выделений приводит к ошибке.

К сожалению libpq будет пытаться полностью кэшировать результирующие наборы в памяти , прежде чем передать управление приложению. Это в дополнение к памяти, которую вы используете в $myArray.

Было предложено использовать LIMIT ... OFFSET ... для уменьшения огибающей памяти; это будет работать, но неэффективно , поскольку может без необходимости дублировать усилия по сортировке на стороне сервера каждый раз, когда запрос переиздается с другим смещением (например, для ответа на LIMIT 10 OFFSET 10000, Postgres все равно придется сортировать весь набор результатов, только чтобы вернуть строки 10000..10010.)

Вместо этого используйте DECLARE ... CURSOR для создания серверного курсора , а затем FETCH FORWARD x для извлечения следующих x строк. Повторите столько раз, сколько необходимо или до тех пор, пока не будут возвращены строки меньше чем x. Не забудьте CLOSE курсор, когда вы закончите, даже когда / если исключение поднято.

Также не SELECT *; если вам нужны только id и name, создайте курсор FOR SELECT id, name (иначе libpq будет без необходимости извлекать и кэшировать столбцы, которые вы никогда не используете, увеличивая объем памяти и общее время запроса.)

Используя курсоры, как показано выше, libpq будет одновременно хранить не более x строк в памяти. Тем не менее, убедитесь, что вы также очищаете $myArray между FETCH es, если это возможно, иначе у вас все еще может быть недостаточно памяти из-за $myArray.

3 голосов
/ 28 марта 2010

Вы можете использовать LIMIT (x) и OFFSET (y)

0 голосов
/ 28 марта 2010

Ошибка означает, что PHP пытается выделить 3 байта, но вся доступная часть этих 512 МБ составляет менее 3 байтов.

Даже если вы делаете это партиями, в зависимости от размера получаемого массива вы все равно можете исчерпать доступную память.

Возможно, вам не нужно получать все записи?

0 голосов
/ 28 марта 2010

Сервер PostgreSQL кэширует результаты запросов до тех пор, пока вы на самом деле не получите их, поэтому добавление их в массив в подобном цикле приведет к исчерпанию памяти, несмотря ни на что. Либо обрабатывайте результаты по одной строке за раз, либо проверяйте длину массива, обрабатывайте результаты, полученные до сих пор, а затем очищайте массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...