姜戈过滤器什么更有效 - .filter(date__range) 或 .all() 并通过 Python 过滤

问题描述

我有一个相当大的数据库表(1M+ 行),在 Django 中过滤结果时遇到问题。

目前过滤逻辑如下:

results = Result.objects.filter(date__range=(date_from,date_to))
for result in results:
    # do stuff

在某些时期它会导致崩溃(可能是由于内存耗尽)

我想知道,将其替换为以下内容会更有效吗:

results = Result.objects.all().order_by('-id')
for result in results:
    if result.date > date_to:
        continue
    if result.date < date_from:
        break
    # do stuff

理论上,由于 .all() 创建了一个惰性 QuerySet,它可能比具有 1M+ 行的数据库中的范围过滤性能更好。 了解这两种情况下的内存消耗也会很有趣。

也许还有另一种解决方案,如何在恒定内存中更有效地做到这一点?

谢谢!

解决方法

理论上,由于 .all() 创建了一个惰性 QuerySet,它可能比具有 1M+ 行的数据库中的范围过滤性能更好。了解这两种情况下的内存消耗也会很有趣。

QuerySet 是惰性的,因为它只会在必要时检索对象,但一旦必须这样做,它将获取所有 项。顺便说一下,不仅 .all() 是惰性的,所有 QuerySet 都是惰性的,所以如果你定义一个 Result.objects.filter(…) 查询集,那么 QuerySet 是惰性的

但不管它是如何实现的,数据库端的过滤效率更高,因为数据库就是为此而设计的,它会导致从数据库到 Python/Django 层的带宽减少。

如果存在内存问题,这可能意味着您的 QuerySet,即使经过过滤,也太大而无法存储在内存中。您可以使用 .iterator(…) method [Django-doc] 加载然后可以处理的批量项目:

results = Result.objects.filter(date__range=(date_from,date_to)).iterator()

for result in results:
    # …
    pass

If 每次都会将 的记录加载到内存中,然后可以进行处理。如果您不存储项目(例如在列表中),那么 Python 可以为下一个块重用内存。