Geheugenlek in PHP bij het ophalen van grote dataset van MySQL

Wanneer ik de volgende code uitvoer voor een gebruikerstabel van ongeveer 60.000 records:

mysql_connect("localhost", "root", "");
mysql_select_db("test");

$result = mysql_query("select * from users");

while ($row = mysql_fetch_object($result)) {
  echo(convert(memory_get_usage(true))."\n");
}


function convert($size) {
  $unit=array('b','kb','mb','gb','tb','pb');
  return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
}

Ik krijg de volgende foutmelding:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes)

Elke gedachte over hoe te voorkomen dat het script extra geheugen nodig heeft bij elke pass door de lus? In mijn echte code probeer ik een CSV-download aan te leveren voor een grote dataset, met een kleine PHP-voorbewerking.

Gelieve niet aan te bevelen PHP's geheugenlimiet te verhogen - het is een slecht idee en, nog belangrijker, zal nog steeds een opwaartse grens creëren voor hoe groot een dataset met deze techniek kan worden verwerkt.

4
De titel van dit bericht is misleidend. Het is geen geheugenlek, je werkt met een enorm resultaat op een manier die verder gaat dan een vooraf ingestelde geheugenlimiet.
toegevoegd de auteur Mike Purcell, de bron
toegevoegd de auteur Jon Black, de bron
Waarom probeer je de zoekopdracht niet te pagineren ???
toegevoegd de auteur Galled, de bron
@Galled - ik dump deze gegevens naar CSV, dus paginering is niet echt een optie.
toegevoegd de auteur Lou Kosak, de bron

3 antwoord

Ik weet niet zeker of dit je probleem zal oplossen, maar heb je overwogen om BOB te gebruiken? ? Het heeft verschillende voordelen; u kunt er meer over lezen hier . Als je die kant op gaat, is er een vergelijkbare vraag over geheugengebruik hier .

1
toegevoegd

mysql_query buffers the entire result set into PHP memory. This is convenient and generally very fast, but you're experiencing a drawback to it.

mysql_unbuffered_query() exists. It doesn't grab the entire result set all at once. It grabs little pieces at a time when you fetch rows from the result set.

1
toegevoegd
Bedankt Chris, dit is precies wat ik zocht. Het lijkt heel vreemd dat het standaardgedrag van MySQL is om extra geheugen vast te maken voor elke rij die wordt opgehaald (zelfs bij het sequentieel ophalen en niet expliciet opslaan van de gegevens in het geheugen), maar daar heb je het.
toegevoegd de auteur Lou Kosak, de bron

Ik heb een soortgelijk probleem gehad. Wat ik deed om het te laten werken was om een ​​tijdelijk bestand te maken (je kunt hash of iets dergelijks gebruiken om de naam bij te houden).

  • Trek 10.000 rijen en plaats ze in een bestand (temp). Zet het in een tijdelijk CSV-bestand.
  • Pagina herladen (via header met specifieke parameters/sessie)
  • Trek nog eens 10.000 rijen en voeg deze toe aan een bestand.
  • Wanneer u het einde van de tabel bereikt - bufferbestand naar gebruiker.

Doe dat in cirkels totdat je alles hebt. Ik moest dit werk doen om twee redenen,

  1. Timeout
  2. Fout met onvoldoende geheugen.

Drawbacks of this method is that it requires many HTTP calls to get data. Also in the mean time there could be rows that have changed etc. It is a pretty "dirty" way of doing it. I'm yet to find something that works better. Hope that helps.

0
toegevoegd