Domanda

La mia app PHP ha uno script di importazione che può importare record.

Al momento, sta importando da un file CSV. Sta leggendo ogni riga del file CSV, una riga alla volta usando fgetcsv, e per ogni riga sta facendo molto di elaborazione su quel record, incluse le query del database, e poi passa al riga successiva. Non dovrebbe essere necessario continuare ad accumulare più memoria.

Dopo l'importazione di circa 2500 record, PHP muore, dicendo che ha superato il limite di memoria (132 MB o giù di lì).

Il file CSV stesso è solo un paio di mega - l'altra elaborazione che accade fa molti confronti di stringhe, differenze, ecc. Ho un enorme quantità di codice che opera su di esso e sarebbe difficile trovare un "campione riproduttivo più piccolo".

Quali sono alcuni buoni modi per trovare e risolvere un problema del genere?

Causa del problema riscontrato

Ho una classe di debug che registra tutte le mie query di database durante il runtime. Quindi quelle stringhe di SQL, lunghe circa 30 KB, rimanevano in memoria. Mi rendo conto che questo non è adatto per gli script progettati per essere eseguiti a lungo.

Potrebbero esserci altre fonti di perdite di memoria, ma sono abbastanza sicuro che questa sia la causa del mio problema.

È stato utile?

Soluzione

Sarebbe utile dare un'occhiata al codice, ma se vuoi eseguire il debug da solo, dai un'occhiata a Xdebug , ti aiuterà a profilare la tua applicazione.

Naturalmente, a seconda di ciò che stai facendo, è possibile che stia accumulando memoria, anche se 132 MB sembrano già elevati per 2500 record. Ovviamente, puoi modificare il limite di memoria in php.ini se necessario.

Quanto è grande il file CSV che stai leggendo? E quali oggetti e che tipo di elaborazione stai facendo?

Altri suggerimenti

Se in realtà sospetti che ci siano solo una o due perdite di memoria nel tuo script che causano il crash, allora dovresti seguire i seguenti passi:

  • Cambia memory_limit in qualcosa di piccolo, come 500 KB
  • Commenta tutte le fasi di elaborazione tranne una che viene applicata a ciascuna riga.
  • Esegui l'elaborazione limitata sull'intero file CSV e verifica se può essere completata.
  • Aggiungi gradualmente più passaggi e osserva se i picchi di utilizzo della memoria sono aumentati.

Esempio:

ini_set('memory_limit', 1024 * 500);
$fp = fopen("test.csv", 'r');
while($row = fgetcsv($fp)) {
    validate_row($row);         // step 1: validate
    // add these back in one by one and keep an eye on memory usage
    //calculate_fizz($row);     // step 2: fizz
    //calculate_buzz($row);     // step 3: buzz
    //triangulate($row);        // step 4: triangulate
}
echo "Memory used: ", memory_get_peak_usage(), "\n";

Lo scenario peggiore è che tutte delle tue fasi di elaborazione sono moderatamente inefficienti e dovrai ottimizzarle tutte.

Dipende da come stai cancellando le variabili dopo averle fatte.

Sembra che tu abbia finito con il record ma stai ancora conservando le informazioni da qualche parte. Usa unset () per cancellare le variabili in caso di dubbio.

Fornisci un esempio di codice di riproduzione minimo per vedere dove sta andando tutta quella memoria se questo non aiuta.

A proposito, produrre l'esempio di codice più piccolo che riprodurrà il problema è un'ottima tecnica di debug perché ti costringe a ripassare il codice, con cura.

potresti provare un'installazione locale di php5.3 e chiamare http://www.php.net/manual/en/function.gc-collect-cycles.php.

  

gc_collect_cycles - Forza la raccolta di eventuali cicli di immondizia esistenti

se la situazione migliora, hai almeno verificato (su) i problemi.

Come stai leggendo il file? Se si utilizza fread / filegetcontents o altre funzioni simili, si consumerà l'intera dimensione del file (o comunque si carica con fread) in memoria poiché l'intero file viene caricato al momento della chiamata. Tuttavia, se usi fgetcsv se leggerai solo una riga alla volta a seconda della lunghezza della riga, questo può essere drammaticamente più facile nella tua memoria.

Assicurati anche di riutilizzare quante più variabili possibili su ciascun loop. Verifica che non vi siano array con grandi quantità di dati.

Come ultima nota assicurati anche di aprire il tuo file prima del tuo ciclo e di chiuderlo dopo le parole:

$fh = fopen(...);
while(true)
{
//...
}
fclose($fh);

Non vuoi davvero farlo:

while(true)
{
$fh = fopen(...);
//...
fclose($fh);
}

E come altri hanno già detto, sarà difficile dirlo senza vedere del codice.

È difficile dire la causa senza vedere alcun codice. Tuttavia, un problema tipico sono i riferimenti ricorsivi, ad es. L'oggetto A punta all'oggetto B e viceversa, il che può causare il malfunzionamento del GC.

Non so come stai attualmente elaborando il file, ma potresti provare a leggere il file solo una riga alla volta. Se leggi l'intero file in una sola volta, potresti consumare più memoria.

Questo è in realtà uno dei motivi per cui spesso preferisco Python per le attività di elaborazione batch.

Sei in grado di cambiare memory_limit nel tuo php.ini?

Inoltre, fare unset ($ var) sulle variabili può liberare memoria? $ Var = null potrebbe anche aiutare?

Vedi anche questa domanda: Cosa c'è di meglio a liberare memoria con PHP: unset () o $ var = null

Avevo lo stesso problema ed era anche dovuto alla profilazione del database (Zend_Db_Profiler_Firebug). Nel mio caso perdeva 1 mb al minuto. questo script avrebbe dovuto funzionare per giorni, quindi si sarebbe bloccato in poche ore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top