这篇讲两个点:

  1. QuerySet 的“惰性加载”与缓存机制
  2. iterator() 的适用场景与坑

QuerySet 是惰性的

QuerySet 只是“待执行的 SQL”。只有在真正迭代时,才会访问数据库。

person_set = Person.objects.filter(first_name="Dave")

上面这行不会发 SQL。直到你做:

for person in person_set:
    print(person.last_name)

QuerySet 缓存机制

QuerySet 在第一次被评估时,会把结果缓存起来。

好处:同一个 QuerySet 重复使用,不会重复发 SQL。

问题:一旦数据量很大,就会占用大量内存。

误用示例(双倍查询)

print([e.headline for e in Entry.objects.all()])
print([e.pub_date for e in Entry.objects.all()])

这会发两次 SQL,且两次结果可能不一致。

正确示例(复用缓存)

queryset = Entry.objects.all()
print([e.headline for e in queryset])
print([e.pub_date for e in queryset])

iterator() 的作用

iterator() 关闭 QuerySet 的缓存,边读边吐,减少内存占用。

适合:一次性遍历大量数据

from blog.models import Article

for i in Article.objects.all().iterator():
    print(i)

注意:

  • iterator() 会忽略 prefetch_related()
  • 再次遍历会重新查询数据库。
  • 对超大表依旧可能慢,建议配合分批游标。

最佳实践

  • 小数据:默认 QuerySet 即可(缓存有利)。
  • 大数据但只遍历一次:用 iterator()
  • 超大表:iterator() + 分批(或用主键游标)。

References