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 шт):
Так можно сократить потребление памяти:
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)