dav*_*ers 104 sql django postgresql django-orm
该表包含大约一千万行.
for event in Event.objects.all():
print event
Run Code Online (Sandbox Code Playgroud)
这导致内存使用量稳定增加到4 GB左右,此时行快速打印.第一行打印之前的漫长延迟令我感到惊讶 - 我预计它几乎可以立即打印出来.
我也尝试过Event.objects.iterator()以同样的方式行事.
我不明白Django加载到内存中的原因或为什么会这样做.我希望Django的通过在数据库级别的结果迭代,which'd意味着结果将在大约(长时间的等待之后,而不是一次全部)以恒定的速率进行打印.
我误解了什么?
(我不知道它是否是相关的,但我使用PostgreSQL.)
ete*_*ode 104
Nate C很接近,但并不完全.
来自文档:
您可以通过以下方式评估QuerySet:
迭代.QuerySet是可迭代的,并且在您第一次迭代它时执行其数据库查询.例如,这将打印数据库中所有条目的标题:
Run Code Online (Sandbox Code Playgroud)for e in Entry.objects.all(): print e.headline
因此,当您第一次进入该循环并获取查询集的迭代形式时,将同时检索您的一千万行.您遇到的等待是Django加载数据库行并为每个行创建对象,然后返回您可以实际迭代的内容.然后你就拥有了记忆中的一切,结果就会溢出来.
从我阅读的文档来看,iterator()除了绕过QuerySet的内部缓存机制之外别无其他.我认为它可能是一个一个接一个的事情,但相反,你的数据库需要1000万次点击.也许不是那么可取.
有效地迭代大型数据集是我们仍然没有完全正确的事情,但是有一些片段可能会对您的目的有用:
mpa*_*paf 38
可能不是更快或更有效,但作为现成的解决方案,为什么不使用django core的Paginator和Page对象记录在这里:
https://docs.djangoproject.com/en/dev/topics/pagination/
像这样的东西:
from django.core.paginator import Paginator
from djangoapp.models import model
paginator = Paginator(model.objects.all(), 1000) # chunks of 1000, you can
# change this to desired chunk size
for page in range(1, paginator.num_pages + 1):
for row in paginator.page(page).object_list:
# here you can do whatever you want with the row
print "done processing page %s" % page
Run Code Online (Sandbox Code Playgroud)
Luk*_*ore 26
Django的默认行为是在评估查询时缓存QuerySet的整个结果.您可以使用QuerySet的迭代器方法来避免此缓存:
for event in Event.objects.all().iterator():
print event
Run Code Online (Sandbox Code Playgroud)
https://docs.djangoproject.com/en/dev/ref/models/querysets/#iterator
iterator()方法计算查询集,然后直接读取结果,而不在QuerySet级别进行缓存.当迭代您只需要访问一次的大量对象时,此方法可以获得更好的性能并显着减少内存.请注意,缓存仍在数据库级别完成.
使用iterator()减少了我的内存使用量,但它仍然高于我的预期.使用mpaf建议的分页器方法使用的内存要少得多,但对于我的测试用例来说要慢2-3倍.
from django.core.paginator import Paginator
def chunked_iterator(queryset, chunk_size=10000):
paginator = Paginator(queryset, chunk_size)
for page in range(1, paginator.num_pages + 1):
for obj in paginator.page(page).object_list:
yield obj
for event in chunked_iterator(Event.objects.all()):
print event
Run Code Online (Sandbox Code Playgroud)
这来自以下文档:http: //docs.djangoproject.com/en/dev/ref/models/querysets/
在您执行评估查询集的操作之前,实际上不会发生数据库活动.
因此,当print event运行时,查询将触发(根据您的命令进行全表扫描)并加载结果.你要求所有的对象,没有办法得到第一个对象而没有获得所有对象.
但如果你做的事情如下:
Event.objects.all()[300:900]
Run Code Online (Sandbox Code Playgroud)
http://docs.djangoproject.com/en/dev/topics/db/queries/#limiting-querysets
然后它会在内部添加sql的偏移量和限制.
这里有很多过时的结果。不确定何时添加,但 Django 的QuerySet.iterator()方法使用具有块大小的服务器端游标来从数据库传输结果。因此,如果您使用的是 postgres,现在应该可以为您处理开箱即用的问题。
Django没有很好的解决方案从数据库中获取大型项目.
import gc
# Get the events in reverse order
eids = Event.objects.order_by("-id").values_list("id", flat=True)
for index, eid in enumerate(eids):
event = Event.object.get(id=eid)
# do necessary work with event
if index % 100 == 0:
gc.collect()
print("completed 100 items")
Run Code Online (Sandbox Code Playgroud)
values_list可用于获取数据库中的所有ID,然后单独获取每个对象.有一段时间,大型对象将在内存中创建,并且不会被垃圾收集直到退出循环.上面的代码在每消耗100个项目后进行手动垃圾收集.
因为那样,整个查询集的对象会立即全部加载到内存中。您需要将查询集分块为更小的可消化位。执行此操作的模式称为“喂汤”。这是一个简短的实现。
def spoonfeed(qs, func, chunk=1000, start=0):
''' Chunk up a large queryset and run func on each item.
Works with automatic primary key fields.
chunk -- how many objects to take on at once
start -- PK to start from
>>> spoonfeed(Spam.objects.all(), nom_nom)
'''
while start < qs.order_by('pk').last().pk:
for o in qs.filter(pk__gt=start, pk__lte=start+chunk):
yeild func(o)
start += chunk
Run Code Online (Sandbox Code Playgroud)
要使用此功能,您需要编写一个对对象执行操作的函数:
def set_population_density(town):
town.population_density = calculate_population_density(...)
town.save()
Run Code Online (Sandbox Code Playgroud)
然后在您的查询集上运行该函数:
spoonfeed(Town.objects.all(), set_population_density)
Run Code Online (Sandbox Code Playgroud)
可以通过func在多个对象上并行执行的多处理来进一步改善这一点。
| 归档时间: |
|
| 查看次数: |
38422 次 |
| 最近记录: |