我正在尝试使用SQL alchemy复制S3大数据集(大于RAM)。 我的约束是:
我只想以高效的方式将数据从数据库传输到S3 。
我可以使用数据集(使用下面的逻辑)正常工作但是使用更大的数据集我遇到了缓冲区问题。
我解决的第一个问题是执行查询通常会将结果缓冲到内存中。我使用fetchmany()方法。
engine = sqlalchemy.create_engine(db_url)
engine.execution_options(stream_results=True)
results=engine.execute('SELECT * FROM tableX;')
while True:
chunk = result.fetchmany(10000)
if not chunk:
break
另一方面,我有一个StringIO缓冲区,我使用fetchmany数据检查。然后我将其内容发送到s3。
from io import StringIO
import boto3
import csv
s3_resource = boto3.resource('s3')
csv_buffer = StringIO()
csv_writer = csv.writer(csv_buffer, delimiter=';')
csv_writer.writerows(chunk)
s3_resource.Object(bucket, s3_key).put(Body=csv_buffer.getvalue())
我遇到的问题本质上是一个设计问题,如何让这些部分协同工作。它甚至可以在同一个运行时吗?
engine = sqlalchemy.create_engine(db_url)
s3_resource = boto3.resource('s3')
csv_buffer = StringIO()
csv_writer = csv.writer(csv_buffer, delimiter=';')
engine.execution_options(stream_results=True)
results=engine.execute('SELECT * FROM tableX;')
while True:
chunk = result.fetchmany(10000)
csv_writer = csv.writer(csv_buffer, delimiter=';')
csv_writer.writerows(chunk)
s3_resource.Object(bucket, s3_key).put(Body=csv_buffer.getvalue())
if not chunk:
break
我可以让它适用于fetchmany的一个循环,但不是几个循环。有什么想法吗?
答案 0 :(得分:2)
我假设通过“让这些部分一起工作”你的意思是你想在S3中使用单个文件而不仅仅是部分文件?您需要做的就是创建一个文件对象,在读取时,将为下一个批处理和缓冲区发出查询。我们可以使用python的生成器:
def _generate_chunks(engine):
with engine.begin() as conn:
conn = conn.execution_options(stream_results=True)
results = conn.execute("")
while True:
chunk = results.fetchmany(10000)
if not chunk:
break
csv_buffer = StringIO()
csv_writer = csv.writer(csv_buffer, delimiter=';')
csv_writer.writerows(chunk)
yield csv_buffer.getvalue().encode("utf-8")
这是你的文件块的流,所以我们需要做的就是将这些(当然懒惰地)拼接到一个文件对象中:
class CombinedFile(io.RawIOBase):
def __init__(self, strings):
self._buffer = ""
self._strings = iter(strings)
def read(self, size=-1):
if size < 0:
return self.readall()
if not self._buffer:
try:
self._buffer = next(self._strings)
except StopIteration:
pass
if len(self._buffer) > size:
ret, self._buffer = self._buffer[:size], self._buffer[size:]
else:
ret, self._buffer = self._buffer, b""
return ret
chunks = _generate_chunks(engine)
file = CombinedFile(chunks)
upload_file_object_to_s3(file)
将文件对象流式传输到S3留给读者练习。 (你可以使用put_object
。)