Python. Как эффективно записать данные в базу из большого файла, обогатив их дополнительным колонками

Есть API к которой генерируются некоторые отчеты по приложениям за день. API возвращает ссылку csv файл, для каждого приложения. Т.е. итерируюсь по списку приложений, запрашиваю отчет для приложения, потом вставка в базу и так для каждой аппки. Файлы разного размера, от пары сотен Кб, до нескольких Гб. Есть достаточно большой файл, 8+ гб, около 24 млн. строк. Проблема в том, что в основном файлы мелкие и большое количество Insert создает излишнюю нагрузку на БД (Redshift)? Т.е. сейчас для каждого приложения за день получаю файл и вставляю его в БД, задача перейти на "пакетную" вставку всех данных за день с помощью команды COPY с S3. Проблема в том, что нужно немного улучшить данные. Добавить пару колонок, соответственно в первую строку названия колонок и для каждой последующей строки добавить константные значения для приложения. Выглядит это примерно вот так:

response = requests.get(response.json()["report_url"], stream=True)
response.raise_for_status()
csv_content = BytesIO(response.content)
original_data = [line.strip().split(b',') for line in csv_content.readlines()]

# store_id,os_name,account известны на момент запроса отчета,  константы для каждого 
# приложения и они действительно нужны в файле, т.к. в сырых данных нет идентификатора 
# который позволил бы связать их с приложением
column_names = [b"store_id",b"os_name",b"account"]
values = [bytes(store_id, encoding="UTF-8"), bytes(os_name, encoding="UTF-8"), bytes(account, encoding="UTF-8")]

# добавляем названия столбцов
original_data[0].extend(column_names)
# добавляем значения
[row.extend(values) for row in original_data[1:]]

#пишем в файл и сохраняем на s3
#потом COPY в таблицу в БД
with tempfile.NamedTemporaryFile(mode='w+b') as report:
    report.writelines(b','.join(row) + b'\n' for row in original_data)

    report.flush()
    report.seek(0)

    bucket_name, key = self.get_s3_path(start, end, store_id, os_name, account)
    self.s3.meta.client.upload_file(report.name, bucket_name, key)
    ....

Для больших файлов помещение данных в список, оказалось весьма ресурсоемкой задачей. Можно как-то сразу писать в файл добавляя нужные столбцы, не используя промежуточный список?

сейчас реализован вариант с копированием в промежуточную таблицу не модифицированных данных, а потом уже на стадии инсерта из промежуточной таблицу в основную добавляю константные значения, но цель как раз отказаться от использования Insert для каждого в отделности в пользу COPY.


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Так можно сократить потребление памяти:

response = requests.get(response.json()["report_url"], stream=True)
response.raise_for_status()

csv_content = BytesIO(response.content)

header_tail = ','.join((b'', b'store_id', b'os_name', b'account')) + b'\n'
line_tail = ','.join((
    b'',
    bytes(store_id, encoding="UTF-8"),
    bytes(os_name, encoding="UTF-8"),
    bytes(account, encoding="UTF-8")
)) + b'\n'

#пишем в файл и сохраняем на s3
#потом COPY в таблицу в БД
with tempfile.NamedTemporaryFile(mode='w+b') as report:
    report.write(next(csv_content).strip() + header_tail)

    for line in csv_content:
        report.write(line.strip() + line_tail)

    report.flush()
    report.seek(0)

    bucket_name, key = self.get_s3_path(start, end, store_id, os_name, account)
    self.s3.meta.client.upload_file(report.name, bucket_name, key)
→ Ссылка