PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

MemoryError ao usar o método read() na leitura de um arquivo JSON de tamanho grande do Amazon S3


Uma economia significativa pode ser obtida evitando o armazenamento de todo o seu arquivo de entrada na memória como uma list de linhas.

Especificamente, essas linhas são terríveis no uso de memória, pois envolvem um uso máximo de memória de bytes objeto o tamanho de todo o seu arquivo, mais uma list de linhas com o conteúdo completo do arquivo também:
file_content = obj['Body'].read().decode('utf-8').splitlines(True)
for line in file_content:

Para um arquivo de texto ASCII de 1 GB com 5 milhões de linhas, no Python 3.3+ de 64 bits, esse é um requisito máximo de memória de aproximadamente 2,3 GB para apenas os bytes objeto, a list , e o str individual s na list . Um programa que precisa de 2,3 vezes mais RAM do que o tamanho dos arquivos que processa não será dimensionado para arquivos grandes.

Para corrigir, altere esse código original para:
file_content = io.TextIOWrapper(obj['Body'], encoding='utf-8')
for line in file_content:

Dado que obj['Body'] parece ser utilizável para streaming lento isso deve remover ambos cópias dos dados completos do arquivo da memória. Usando TextIOWrapper significa obj['Body'] é lido e decodificado lentamente em pedaços (de alguns KB por vez), e as linhas também são iteradas lentamente; isso reduz as demandas de memória para uma quantidade pequena e amplamente fixa (o custo máximo de memória dependeria do comprimento da linha mais longa), independentemente do tamanho do arquivo.

Atualização:

Parece StreamingBody não implementa o io.BufferedIOBase ABC. Ele tem sua própria API documentada no entanto, que pode ser usado para um propósito semelhante. Se você não conseguir fazer o TextIOWrapper fazer o trabalho para você (é muito mais eficiente e simples se puder ser feito para funcionar), uma alternativa seria fazer:
file_content = (line.decode('utf-8') for line in obj['Body'].iter_lines())
for line in file_content:

Ao contrário de usar TextIOWrapper , ele não se beneficia da decodificação em massa de blocos (cada linha é decodificada individualmente), mas, caso contrário, ainda deve obter os mesmos benefícios em termos de uso reduzido de memória.