psycopg2
模块从Postgres数据库中读取数据。我需要对一列中的所有行执行某些操作,该列有超过100万行。我想知道
cur.fetchall()
会失败或导致服务器崩溃吗?(因为我的RAM可能无法容纳那么多数据)q="SELECT names from myTable;"
cur.execute(q)
rows=cur.fetchall()
for row in rows:
doSomething(row)
有更聪明的方法来做这件事吗?
psycopg2
模块从Postgres数据库中读取数据。我需要对一列中的所有行执行某些操作,该列有超过100万行。cur.fetchall()
会失败或导致服务器崩溃吗?(因为我的RAM可能无法容纳那么多数据)q="SELECT names from myTable;"
cur.execute(q)
rows=cur.fetchall()
for row in rows:
doSomething(row)
有更聪明的方法来做这件事吗?
Burhan提出的解决方案通过仅获取单行数据来减少大型数据集的内存使用:
row = cursor.fetchone()
然而,我注意到逐行获取数据会显著减慢速度。 我通过互联网连接访问外部数据库,这可能是其中一个原因。
具有服务器端游标并获取一堆行被证明是最有效的解决方案。 您可以更改 SQL 语句(如 Alecxe 的答案中所述),但也可以使用 psycopg2 提供的功能进行纯 Python 方法:
cursor = conn.cursor('name_of_the_new_server_side_cursor')
cursor.execute(""" SELECT * FROM table LIMIT 1000000 """)
while True:
rows = cursor.fetchmany(5000)
if not rows:
break
for row in rows:
# do something with row
pass
您可以在psycopg2 wiki中了解更多关于服务器端游标的内容。
考虑使用服务器端游标:
当执行数据库查询时,Psycopg游标通常会提取由后端返回的所有记录,并将它们传输到客户端进程。如果查询返回了大量数据,则客户端将分配相应数量的内存。
如果数据集过大以至于在客户端无法实际处理,那么可以创建一个服务器端游标。使用这种类型的游标,可以仅向客户端传输受控制的数据量,使得可以检查大型数据集而不必将其全部保存在内存中。
这是一个例子:
cursor.execute("DECLARE super_cursor BINARY CURSOR FOR SELECT names FROM myTable")
while True:
cursor.execute("FETCH 1000 FROM super_cursor")
rows = cursor.fetchall()
if not rows:
break
for row in rows:
doSomething(row)
fetchall()
方法最多获取 arraysize
条记录,为了避免对数据库造成巨大的负担,你可以分批获取记录,或者逐行遍历游标直到全部获取完毕:
row = cur.fetchone()
while row:
# do something with row
row = cur.fetchone()
fetchmany
管理速度的方法。itersize
,以一次加载多行数据,就像fetchmany
一样,但只需使用单个循环for rec in cursor
即可隐式执行fetchnone()
。where(ordered_val =%(last_seen_val)s and primary_key>%(last_seen_pk)s OR ordered_val>%(last_seen_val)s)
增强下一个查询。
这至少对于库来说是误导性的,文档应该有关于此的描述。我不知道为什么它没有出现。
如果没有需要交互式向前/向后滚动的情况,不确定命名游标是否适合?我可能在这里错了。
fetchmany
循环很繁琐,但我认为这是最好的解决方案。为了让生活更轻松,您可以使用以下内容:
from functools import partial
from itertools import chain
# from_iterable added >= python 2.7
from_iterable = chain.from_iterable
# util function
def run_and_iterate(curs, sql, parms=None, chunksize=1000):
if parms is None:
curs.execute(sql)
else:
curs.execute(sql, parms)
chunks_until_empty = iter(partial(fetchmany, chunksize), [])
return from_iterable(chunks_until_empty)
# example scenario
for row in run_and_iterate(cur, 'select * from waffles_table where num_waffles > %s', (10,)):
print 'lots of waffles: %s' % (row,)
阅读评论和答案时,我想澄清一些关于fetchone
和服务器端光标的问题,以供未来读者参考。
在使用普通游标(客户端)时,Psycopg
获取被后端返回的所有记录,并将它们传输到客户端进程。整个记录都缓存在客户端的内存中,当你执行像curs.execute('SELECT * FROM ...'
这样的查询时。
这个问题也证实了这一点。
所有的fetch*
方法都是用来访问存储的数据的。
问:fetchone
如何帮助我们更好地利用内存?
答:它仅从存储的数据中获取一个记录,并创建一个单独的Python对象,并将其交给你的Python代码,而fetchall
将从该数据获取并创建n个Python对象,并一次性将它们全部交给你。
如果你的表有1,000,000条记录,那么在内存中会发生以下情况:
curs.execute --> whole 1,000,000 result set + fetchone --> 1 Python object
curs.execute --> whole 1,000,000 result set + fetchall --> 1,000,000 Python objects
fetchone
有所帮助,但我们仍然需要将整个记录存储在内存中。这就是服务器端游标发挥作用的地方:
因此,您不会在一个块中获得整个结果集。PostgreSQL也有自己的游标概念(有时也称为portal)。创建数据库游标时,查询不一定完全处理:服务器可能只能按需生成结果。仅传输客户端请求的结果:如果查询结果非常大,但客户端只需要前几条记录,则可以仅传输它们。 ... 它们的接口相同,但在幕后,它们发送命令以控制服务器上游标的状态(例如在获取新记录或使用scroll()移动时)。
缺点是服务器需要跟踪部分处理的结果,因此它在服务器上使用更多的内存和资源。