postgresql python postgres我能用fetchall()获取100万行吗?

2sbarzqh  于 2022-12-22  发布在  PostgreSQL
关注(0)|答案(6)|浏览(283)

我正在使用python中的psycopg2模块读取postgres数据库,我需要对一列中的所有行进行一些操作,该列有超过100万行。
我想知道cur.fetchall()是否会失败或导致我的服务器宕机?(因为我的RAM可能不足以容纳所有这些数据)

q="SELECT names from myTable;"
cur.execute(q)
rows=cur.fetchall()
for row in rows:
    doSomething(row)

做这件事的更聪明的方法是什么?

wwwo4jvm

wwwo4jvm1#

Burhan指出的解决方案通过只获取单行来减少大型数据集的内存使用:
行=游标.fetchone()
但是,我注意到逐个获取行的速度明显变慢,我通过互联网连接访问外部数据库,这可能是原因之一。
拥有服务器端游标和读取成束的行被证明是性能最好的解决方案,你可以修改sql语句(如alecxe answers),但也有纯粹的python方法使用psycopg2提供的特性:

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中找到更多关于服务器端游标的信息吗

fiei3ece

fiei3ece2#

考虑使用服务器端游标:
在执行数据库查询时,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)
ivqmmu1c

ivqmmu1c3#

fetchall()的读取上限为arraysize,因此,为了防止对数据库的大量访问,您可以在可管理的批处理中读取行,或者简单地遍历游标直到其耗尽:

row = cur.fetchone()
while row:
   # do something with row
   row = cur.fetchone()
hyrbngr7

hyrbngr74#

下面是用于simple server side cursor的代码,具有fetchmany管理的速度。
原则是在Psycopg2中使用 named cursor,并为它提供一个良好的itersize,以便像fetchmany那样一次加载多行,但使用for rec in cursor的单个循环执行隐式fetchnone()
有了这个代码,我可以在1小时内从数十亿行的表中查询1.5亿行,内存为200兆。

baubqpgj

baubqpgj5#

  • 编辑 *:使用fetchmany(沿着fetchone()和fetchall(),即使有行限制(arraysize)仍将发送整个结果集,使其保持在客户端(存储在底层C库中,我认为是libpq),用于任何额外的fetchmany()调用等。不使用命名游标(这需要一个打开的事务),则必须在sql中使用limit和order-by,然后分析结果并使用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,)
bejyjqdl

bejyjqdl6#

当我阅读评论和答案时,我想我应该为未来的读者澄清一些关于fetchone和服务器端游标的问题。
使用普通游标(客户端),Psycopg获取后端返回的所有记录,并将它们传输到客户端进程。当您执行类似curs.execute('SELECT * FROM ...'的查询时,所有记录都缓冲在客户端内存中。
This question也证实了这点。
所有fetch*方法都是用于访问 * 此存储数据 * 的。

**问:**那么fetchone如何帮助我们提高内存?
**答:**它只从存储的数据中获取一条记录,创建一个Python对象,然后交给你Python代码,而fetchall将从这些数据中获取并创建 n 个Python对象,然后把它们都交给你。

如果你的表有100万条记录,内存中的情况是这样的:

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()移动时)。
因此,您不会在一个块中获得整个结果集。
缺点:
缺点是服务器需要跟踪部分处理的结果,因此会占用服务器上**更多的内存和资源。

相关问题