Python, postgress, pandas — куда утекает память?

Надо вычитать много данных из БД (десятки гигабайт) и сохранить их в файлики. Пытаюсь делать это вот в таком цикле, при этом на каждой итерации вычитывается относительно небольшое количество данных (не больше 5 гб), но размер памяти выделяемый процессу всё-равно постоянно увеличивается и в конце-концов система уходит в свап и всё останавливается.
import config
import pandas as pd
import os
import gc

for station in config.STATIONS_LIST:
    sql_query = f"select * from table where  where station = '{station}'"
    df = pd.read_sql(sql_query, con=connection_pg)
    filename = f'data_{station}'
    filename_with_path = os.path.join(config.OUTPUT_PATH, filename)
    compression_options = dict(method='zip', archive_name=f'{filename}.csv')
    df.to_csv(f'{filename_with_path}.zip', compression=compression_options, index=False)
    del df
    gc.collect()
  • Вопрос задан
  • 234 просмотра
Решения вопроса 1
@Arlekcangp
Разработчик, Лид, Архитектор ПО
Я не специалист по пайтону, но присматриваюсь и ваш код меня заинтересовал. Немного погуглив я нашел такой похожий вопрос на SO ( https://stackoverflow.com/questions/39100971/how-d... ), но c более простым кодом:
import pandas
df = pandas.read_csv('large_txt_file.txt')
del df

Уже этого достаточно, что бы память не возвращалась в ОС. Автор вопроса подозревал Pandas, но как пояснили в ответах, это особенность самого пайтона:
Reducing memory usage in Python is difficult, because Python does not actually release memory back to the operating system. If you delete objects, then the memory is available to new Python objects, but not free()'d back to the system (see this question).

Т е если вы смотрите количество используемой процессом памяти, то оно будет только увеличиваться. Первое, что я бы попробовал, это поменять ваш код так:
for station in config.STATIONS_LIST:
    sql_query = f"select * from table where  where station = '{station}'"
    df = pd.read_sql(sql_query, con=connection_pg)
    filename = f'data_{station}'
    filename_with_path = os.path.join(config.OUTPUT_PATH, filename)
    compression_options = dict(method='zip', archive_name=f'{filename}.csv')
    df.to_csv(f'{filename_with_path}.zip', compression=compression_options, index=False)
    <b>df = ' '</b>
    gc.collect()

Т е не удалять переменную, а переприсвоить. Некоторые говорят, что это помогает (если честно, мне в такое с трудом верится, но я не знаю пайтона) Среди других рекомендаций: загружать данные меньшими порциями и офлоудить работу другому процессу, который затем убивается и память освобождается ОС. (на мой взгляд способ хороший, хоть и не слишком архитектурно-правильный, но гарантировано добавит стабильности и застрахует даже от будущих утечек, если они появятся либо в вашем коде либо в новых версиях библиотек)
Другой вопрос, почему это увеличение не останавливается. Если это всё дело происходит на linux то я бы попробовал ограничить пайтону память (первое что нагуглилось: https://www.geeksforgeeks.org/python-how-to-put-li...) и посмотрел будет ли при этом интерпретатор умирать по причине недостатка памяти. Если будет, то на SO рекомендовали такое средство: https://mg.pov.lt/objgraph/ Этим можно посмотреть что именно потребляет память.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы