PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Erreur de mémoire lors de l'utilisation de la méthode read() lors de la lecture d'un fichier JSON de grande taille à partir d'Amazon S3

Des économies importantes peuvent être réalisées en évitant de mettre tout votre fichier d'entrée en mémoire sous forme de list de lignes.

Plus précisément, ces lignes sont terribles sur l'utilisation de la mémoire, en ce sens qu'elles impliquent une utilisation maximale de la mémoire d'un bytes objet la taille de votre fichier entier, plus une list de lignes avec le contenu complet du fichier également :

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

Pour un fichier texte ASCII de 1 Go avec 5 millions de lignes, sur Python 3.3+ 64 bits, il s'agit d'un besoin maximal en mémoire d'environ 2,3 Go pour seulement les bytes objet, la list , et la str individuelle s dans la list . Un programme qui a besoin de 2,3 fois plus de RAM que la taille des fichiers qu'il traite ne sera pas adapté aux fichiers volumineux.

Pour corriger, remplacez ce code d'origine par :

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

Étant donné que obj['Body'] semble être utilisable pour le streaming paresseux cela devrait supprimer les deux copies des données complètes du fichier à partir de la mémoire. Utilisation de TextIOWrapper signifie obj['Body'] est lu et décodé paresseusement en morceaux (de quelques Ko à la fois), et les lignes sont également itérées paresseusement ; cela réduit les demandes de mémoire à une petite quantité largement fixe (le coût maximal de la mémoire dépend de la longueur de la ligne la plus longue), quelle que soit la taille du fichier.

Mise à jour :

Il ressemble à StreamingBody n'implémente pas le io.BufferedIOBase ABC. Il a sa propre API documentée cependant, cela peut être utilisé à des fins similaires. Si vous ne pouvez pas créer le TextIOWrapper faire le travail pour vous (c'est beaucoup plus efficace et simple si cela peut fonctionner), une alternative serait de faire :

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

Contrairement à l'utilisation de TextIOWrapper , il ne bénéficie pas du décodage en bloc des blocs (chaque ligne est décodée individuellement), mais sinon, il devrait toujours obtenir les mêmes avantages en termes de réduction de l'utilisation de la mémoire.