这篇讲两个点:
- QuerySet 的“惰性加载”与缓存机制
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()+ 分批(或用主键游标)。