Kể từ khi collection
bị thu hẹp với cùng tốc độ mà rows
đang tăng, mức sử dụng bộ nhớ của bạn sẽ ổn định.Cuộc gọi gc.collect()
sẽ không tạo ra nhiều khác biệt.
Quản lý bộ nhớ trong CPython là tinh tế. Chỉ vì bạn loại bỏ các tham chiếu và chạy một chu trình thu thập không nhất thiết có nghĩa là bộ nhớ sẽ được trả lại cho hệ điều hành. Xem this answer for details.
Để thực sự lưu bộ nhớ, bạn nên cấu trúc mã này xung quanh các trình tạo và trình lặp thay vì danh sách các mục lớn. Tôi rất ngạc nhiên khi bạn nói rằng bạn đang có thời gian chờ kết nối vì việc tìm nạp tất cả các hàng sẽ không mất nhiều thời gian hơn tìm nạp hàng tại một thời điểm và thực hiện quá trình xử lý đơn giản mà bạn đang thực hiện. Có lẽ chúng ta nên xem xét mã tìm nạp db của bạn?
Nếu việc xử lý hàng-tại-một thời gian thực sự không phải là khả năng, thì ít nhất giữ dữ liệu của bạn như là một deque bất biến và thực hiện tất cả xử lý trên nó với máy phát và trình lặp.
Tôi sẽ phác thảo các phương pháp tiếp cận khác nhau này.
Trước hết, một số chức năng chung:
# 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)
Bây giờ lý tưởng là hàng-at-a-thời gian xử lý, như thế này:
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()
này sẽ đưa số tiền ít nhất có thể xảy ra bộ nhớ - chỉ một hàng tại một thời điểm.
Nếu bạn thực sự cần phải lưu trữ toàn bộ resultset, bạn có thể thay đổi mã hơi:
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)
Bây giờ chúng tôi thu thập tất cả các kết quả vào collection
đầu tiên mà vẫn hoàn toàn trong bộ nhớ cho toàn bộ chạy chương trình.
Tuy nhiên, chúng tôi cũng có thể sao chép cách tiếp cận của bạn trong việc xóa các mục bộ sưu tập khi chúng được sử dụng. Chúng tôi có thể giữ cùng một "hình dạng mã" bằng cách tạo một trình tạo mà làm trống bộ sưu tập nguồn của nó khi nó hoạt động. Nó sẽ giống như thế này:
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)
Bây giờ bạn có thể dễ dàng thay thế drain(collection)
cho collection
khi bạn muốn giải phóng bộ nhớ khi bạn đi. Sau khi drain(collection)
bị cạn kiệt, đối tượng collection
sẽ trống.
Tôi không có nghĩa là để được snide nhưng tại sao bạn không thử nó và xem? –
figured ai đó có thể biết cho dù đó là tổng thể giá trị nó với các chi phí của GC hoặc một số công cụ nội bộ python xảy ra mà tôi đã có thể bỏ qua. – tipu
Nếu có thể, có thể đáng xem xét khả năng sử dụng các trình vòng lặp và thay vì xử lý tất cả các bộ dữ liệu 40k cùng một lúc, hãy xây dựng danh sách và xử lý chúng cùng một lúc. Điều đó sẽ có thêm một số phức tạp, và có thể không đáng để nỗ lực tham gia. – Moshe