Я пытаюсь импортировать большой размер JSON FILE из Amazon S3 в AWS RDS-PostgreSQL с помощью Python. Но эти ошибки возникли,
Traceback (последний последний вызов):
Файл "my_code.py", строка 67, в
file_content = obj ['Body']. read(). decode ('utf-8'). splitlines (True)
Файл "/home/user/asd-to-qwe/fgh-to-hjk/env/local/lib/python3.6/site-packages/botocore/response.py", строка 76, при чтении
chunk = self._raw_stream.read(amt)
Файл "/home/user/asd-to-qwe/fgh-to-hjk/env/local/lib/python3.6/site-packages/botocore/vendored/requests/packages/urllib3/response.py", строка 239, в чтении
data = self._fp.read()
Файл "/usr/lib64/python3.6/http/client.py", строка 462, при чтении
s = self._safe_read (self.length)
Файл "/usr/lib64/python3.6/http/client.py", строка 617, в _safe_read
return b "". join (s)
MemoryError
//my_code.py
import sys
import boto3
import psycopg2
import zipfile
import io
import json
s3 = boto3.client('s3', aws_access_key_id=<aws_access_key_id>, aws_secret_access_key=<aws_secret_access_key>)
connection = psycopg2.connect(host=<host>, dbname=<dbname>, user=<user>, password=<password>)
cursor = connection.cursor()
bucket = sys.argv[1]
key = sys.argv[2]
obj = s3.get_object(Bucket=bucket, Key=key)
def insert_query(data):
query = """
INSERT INTO data_table
SELECT
(src.test->>'url')::varchar, (src.test->>'id')::bigint,
(src.test->>'external_id')::bigint, (src.test->>'via')::jsonb
FROM (SELECT CAST(%s AS JSONB) AS test) src
"""
cursor.execute(query, (json.dumps(data),))
if key.endswith('.zip'):
zip_files = obj['Body'].read()
with io.BytesIO(zip_files) as zf:
zf.seek(0)
with zipfile.ZipFile(zf, mode='r') as z:
for filename in z.namelist():
with z.open(filename) as f:
for line in f:
insert_query(json.loads(line.decode('utf-8')))
if key.endswith('.json'):
file_content = obj['Body'].read().decode('utf-8').splitlines(True)
for line in file_content:
insert_query(json.loads(line))
connection.commit()
connection.close()
Есть ли какие-либо решения этих проблем? Любая помощь сделана, спасибо вам большое!
Значительная экономия может быть достигнута, если вы не вложите весь свой входной файл в память в виде list
строк.
В частности, эти строки ужасны при использовании памяти, поскольку они предполагают использование пиковой памяти объекта bytes
размером всего вашего файла плюс list
строк с полным содержимым файла:
file_content = obj['Body'].read().decode('utf-8').splitlines(True)
for line in file_content:
Для текстового файла ASCII емкостью 1 ГБ с 5 миллионами строк на 64-битном Python 3. 3+ требуется максимальная потребность в памяти примерно 2,3 ГБ только для объекта bytes
, list
и отдельной str
в list
. Программа, которая требует в 2,3 раза больше ОЗУ, чем размер обрабатываемых файлов, не будет масштабироваться для больших файлов.
Чтобы исправить, измените этот исходный код на:
file_content = io.TextIOWrapper(obj['Body'], encoding='utf-8')
for line in file_content:
Учитывая, что obj['Body']
можно использовать для ленивой потоковой передачи, это должно удалить обе копии полных файлов из памяти. Использование TextIOWrapper
означает, что obj['Body']
лениво читается и декодируется в кусках (по несколько килобайт за раз), а также прокручиваются также лениво; это уменьшает потребность в памяти до небольшой, в значительной степени фиксированной суммы (пиковая стоимость памяти будет зависеть от длины самой длинной линии), независимо от размера файла.
Обновить:
Похоже, StreamingBody
не реализует io.BufferedIOBase
ABC. У этого есть свой собственный документированный API, хотя, который может использоваться для аналогичной цели. Если вы не можете заставить TextIOWrapper
выполнять эту работу для вас (это намного эффективнее и проще, если ее можно заставить работать), альтернативой было бы сделать следующее:
file_content = (line.decode('utf-8') for line in obj['Body'].iter_lines())
for line in file_content:
В отличие от использования TextIOWrapper
, он не использует массовое декодирование блоков (каждая строка декодируется отдельно), но в остальном она должна по-прежнему достигать тех же преимуществ с точки зрения сокращения использования памяти.
io
ABC . Вы можете обернуть StreamingBody
в простой файловый класс, который реализует ABC BufferedIOBase
, или, если он уже очень близок к тому, что вам нужно, monkey-patch в нужном материале (например, obj['Body'].readable = True
для установки этот конкретный атрибут).