【发布时间】:2021-08-05 05:14:56
【问题描述】:
我有一个带有 PostgreSQL DB 的 Django 设置。 其中一个表包含大量数据(>1e9 行),我需要有效地迭代其中的一个大子集。
目前,当我尝试选择大量数据时,它开始缓冲结果并且我的计算机内存不足。
如果我在 QuerySet 上使用 .iterator(),它就会挂起。
如果我尝试使用原始 SQL 和 fetchall(),它也会开始缓冲。
我相信 Django 将 psycopg2 用于具有 cursor.itersize 参数的 PostgreSQL,但是当我尝试在 Django 中将它与 cursor 一起使用时,它什么也没做。
我知道问题不在数据库端,因为我可以使用psql(使用-A --variable="FETCH_COUNT=10000")执行查询,并且它会立即开始加载而不使用任何内存。
额外信息:
- 该表有 >10 列,但我只需要其中 2 列,因此如果可以仅获取选定的以加快加载速度,那就太好了。
编辑:使用psycopg2 服务器端光标似乎可以工作,但速度较慢且丑陋:How can I use server-side cursors with django and psycopg2?
编辑 2:这是现在为我工作的代码,但非常难看:
def get_stuff():
def fetch_from_server_cursor(cursor, cursor_name, fetch_size=10_000):
while True:
cursor.execute(f"FETCH {fetch_size} FROM {cursor_name}")
chunk = cursor.fetchall()
if not chunk:
return
yield from chunk
with transaction.atomic(), connection.cursor() as cursor:
cursor_name = "my_cursor"
cursor.execute(
f"""
DECLARE {cursor_name} CURSOR FOR
SELECT first_column, second_column
FROM {MyModel.objects.model._meta.db_table}
"""
)
yield from fetch_from_server_cursor(cursor, cursor_name)
编辑 3:这是 Django 模型,N.B.我在 DB 的表上使用 Timescale,它会自动在 TimeScaleDateTimeField 上创建索引:
class MyModel(models.Model):
first_column = models.IntegerField()
second_column = models.TimeScaleDateTimeField()
third_column = models.URLField(null=True, blank=True)
...
class Meta:
ordering = ("second_column",)
【问题讨论】:
-
它挂起的原因是因为Python在处理1e9项时非常慢。所以迭代器确实会将它分成块,但很可能你会迭代接收到的数据,而 Python 由于其动态特性,在这方面非常慢。
-
这就是我要问的,我如何让它遍历数据,类似于 python 生成器,而不是试图一次获取所有数据。
-
对于
.iterator(),它不会“挂起”,因为最终迭代将结束,Python 处理所有这些元素需要花费很多时间。 -
参见例如
sum(range(1, 1000000000)),计算总和需要几分钟,range也是一个迭代器。现在对于 Django,它甚至会花费 更多 时间,因为它将数据包装在模型对象等中。 -
@Rizhiy 请在您的问题中添加minimal reproducible example(即您已编写或正在尝试运行的代码),当您的问题与代码有关时,有时代码胜过文字;)
标签: python django postgresql psycopg2