PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

MemoryError quando si utilizza il metodo read() nella lettura di un file JSON di grandi dimensioni da Amazon S3

È possibile ottenere un risparmio significativo evitando di riporre in memoria l'intero file di input come un list di righe.

In particolare, queste righe sono terribili sull'utilizzo della memoria, in quanto implicano un picco di utilizzo della memoria di un bytes obiettare la dimensione dell'intero file, più un list di righe anche con il contenuto completo del file:

file_content = obj['Body'].read().decode('utf-8').splitlines(True)
for line in file_content:

Per un file di testo ASCII da 1 GB con 5 milioni di righe, su Python 3.3+ a 64 bit, è un requisito di memoria di picco di circa 2,3 GB per solo i bytes oggetto, l'list e il singolo str s nell'list . Un programma che necessita di 2,3 volte la quantità di RAM rispetto alle dimensioni dei file che elabora non verrà ridimensionato a file di grandi dimensioni.

Per correggere, cambia il codice originale in:

file_content = io.TextIOWrapper(obj['Body'], encoding='utf-8')
for line in file_content:

Dato che obj['Body'] sembra essere utilizzabile per lo streaming lento questo dovrebbe rimuovere entrambi copie dei dati del file completo dalla memoria. Utilizzo di TextIOWrapper significa obj['Body'] viene letto e decodificato pigramente in blocchi (di pochi KB alla volta) e anche le righe vengono ripetute pigramente; questo riduce le richieste di memoria a una quantità piccola, in gran parte fissa (il costo di picco della memoria dipenderebbe dalla lunghezza della linea più lunga), indipendentemente dalle dimensioni del file.

Aggiornamento:

Sembra StreamingBody non implementa io.BufferedIOBase ABC. Ha una propria API documentata tuttavia, che può essere utilizzato per uno scopo simile. Se non riesci a creare il TextIOWrapper fa il lavoro per te (è molto più efficiente e semplice se può essere fatto funzionare), un'alternativa sarebbe fare:

file_content = (line.decode('utf-8') for line in obj['Body'].iter_lines())
for line in file_content:

Diversamente dall'utilizzo di TextIOWrapper , non beneficia della decodifica in blocco dei blocchi (ogni riga viene decodificata individualmente), ma in caso contrario dovrebbe comunque ottenere gli stessi vantaggi in termini di utilizzo ridotto della memoria.