PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Speicherfehler bei Verwendung der read()-Methode beim Lesen einer großen JSON-Datei aus Amazon S3

Sie können erhebliche Einsparungen erzielen, indem Sie vermeiden, Ihre gesamte Eingabedatei als list in den Speicher zu schlürfen von Linien.

Insbesondere sind diese Zeilen für die Speichernutzung schrecklich, da sie eine Spitzenspeichernutzung von bytes beinhalten Objekt die Größe Ihrer gesamten Datei, plus eine list Zeilen mit dem kompletten Inhalt der Datei:

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

Für eine 1 GB große ASCII-Textdatei mit 5 Millionen Zeilen auf 64-Bit-Python 3.3+ ist das ein Spitzenspeicherbedarf von ungefähr 2,3 GB für nur die bytes Objekt, die list , und die einzelnen str s in der list . Ein Programm, das 2,3-mal so viel RAM benötigt wie die Größe der Dateien, die es verarbeitet, wird nicht auf große Dateien skaliert.

Um das Problem zu beheben, ändern Sie diesen ursprünglichen Code in:

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

Angesichts dessen obj['Body'] scheint für Lazy Streaming verwendbar zu sein dies sollte beide entfernen Kopien der vollständigen Dateidaten aus dem Speicher. Verwenden von TextIOWrapper bedeutet obj['Body'] wird träge gelesen und in Blöcken (mit jeweils wenigen KB) decodiert, und die Zeilen werden ebenfalls träge iteriert; dies reduziert den Speicherbedarf auf eine kleine, weitgehend festgelegte Menge (die Spitzenspeicherkosten würden von der Länge der längsten Zeile abhängen), unabhängig von der Dateigröße.

Aktualisierung:

Es sieht aus wie StreamingBody implementiert nicht die io.BufferedIOBase ABC. Es hat seine eigene dokumentierte API aber das kann für einen ähnlichen Zweck verwendet werden. Wenn Sie den TextIOWrapper nicht erstellen können die Arbeit für Sie erledigen (es ist viel effizienter und einfacher, wenn es zum Laufen gebracht werden kann), wäre eine Alternative:

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

Im Gegensatz zur Verwendung von TextIOWrapper , es profitiert nicht von der Massendekodierung von Blöcken (jede Zeile wird einzeln dekodiert), aber ansonsten sollte es immer noch die gleichen Vorteile in Bezug auf die reduzierte Speichernutzung erzielen.