由于它以与增长collection
相同的速度缩小,因此rows
您的内存使用量将保持稳定。这个gc.collect()
电话不会有太大的不同。
CPython 中的内存管理很微妙。仅仅因为您删除引用并运行收集周期并不一定意味着内存将返回给操作系统。有关详细信息,请参阅此答案。
为了真正节省内存,您应该围绕生成器和迭代器而不是大型项目列表来构建此代码。我很惊讶你说你有连接超时,因为获取所有行不应该比一次获取一行并执行你正在做的简单处理花费更多的时间。也许我们应该看看你的 db-fetching 代码?
如果一次一行的处理确实不可能,那么至少将您的数据保留为不可变的双端队列,并使用生成器和迭代器对其执行所有处理。
我将概述这些不同的方法。
首先,一些常用的功能:
# if you don't need random-access to elements in a sequence
# a deque uses less memory and has faster appends and deletes
# from both the front and the back.
from collections import deque
from itertools import izip, repeat, islice, chain
import re
re_redshift_chars = re.compile(r'[abcdefg]')
def istrjoin(sep, seq):
"""Return a generator that acts like sep.join(seq), but lazily
The separator will be yielded separately
"""
return islice(chain.from_iterable(izip(repeat(sep), seq)), 1, None)
def escape_redshift(s):
return re_redshift_chars.sub(r'\\\g<0>', s)
def tabulate(row):
return "\t".join(escape_redshift(str(v)) if v is not None else '' for v in row)
现在理想的是一次行处理,如下所示:
cursor = db.cursor()
cursor.execute("""SELECT * FROM bigtable""")
rowstrings = (tabulate(row) for row in cursor.fetchall())
lines = istrjoin("\n", rowstrings)
file_like_obj.writelines(lines)
cursor.close()
这将占用尽可能少的内存——一次只占用一行。
如果确实需要存储整个结果集,可以稍微修改一下代码:
cursor = db.cursor()
cursor.execute("SELECT * FROM bigtable")
collection = deque(cursor.fetchall())
cursor.close()
rowstrings = (tabulate(row) for row in collection)
lines = istrjoin("\n", rowstrings)
file_like_obj.writelines(lines)
现在我们将所有结果收集到collection
first 中,它完全保留在内存中以供整个程序运行。
但是,我们也可以复制您在使用时删除收集项的方法。我们可以通过创建一个在工作时清空其源集合的生成器来保持相同的“代码形状” 。它看起来像这样:
def drain(coll):
"""Return an iterable that deletes items from coll as it yields them.
coll must support `coll.pop(0)` or `del coll[0]`. A deque is recommended!
"""
if hasattr(coll, 'pop'):
def pop(coll):
try:
return coll.pop(0)
except IndexError:
raise StopIteration
else:
def pop(coll):
try:
item = coll[0]
except IndexError:
raise StopIteration
del coll[0]
return item
while True:
yield pop(coll)
现在,您可以轻松地在您想随时释放内存时进行drain(collection)
替换。collection
用完后drain(collection)
,collection
对象将为空。