我有一个带有 PostgreSQL DB 的 Django 设置。 其中一个表包含大量数据(>1e9 行),我需要有效地迭代其中的一个大子集。
目前,当我尝试选择大量数据时,它会开始缓冲结果,并且我的计算机内存不足。 如果我在 QuerySet 上使用 .iterator() ,它就会挂起。 如果我尝试使用原始 SQL 和 fetchall(),它也会开始缓冲。
我相信 Django 对 PostgreSQL 使用 psycopg2
,它有 cursor.itersize
参数,但是当我尝试在 Django 中将它与游标一起使用时,它不会执行任何操作。
我知道问题不在数据库端,因为我可以使用 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 模型,注意:我在数据库中的表上使用 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",)
最佳答案
The table has [more than] 10 columns, but I only need 2 of them, so if it is possible to only fetch selected for faster loading it would be nice.
您可以使用.only(…)
[Django-doc]来做到这一点仅选择列的子集,例如:
for item in MyModel.objects.only('pk', '<i>other_column</i>').iterator():
print((item.pk, item.<i>other_column</i>))
这将减少数据库和应用程序层之间的一些带宽。但无论如何,109 项在 Python 中通常不太可行。例如,如果我们简单地对此类范围内的项目进行求和 (sum(range(1000000000))
),则需要约 14 秒,但这是一个非常简单的生成器。 Django 将为每条记录从数据库中读取内容,创建一个模型对象,并相应地设置字段,因此这很容易需要几分钟甚至几小时。
关于python - 从Django ORM中高效提取大量数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67544331/