Ste*_*ano 38 sql database django
我有一张很大的桌子.它目前在MySQL数据库中.我用django.
我需要迭代表中的每个元素来预先计算一些特定的数据(也许如果我更好,我可以做其他事情,但这不是重点).
我希望通过不断使用内存来尽可能快地保持迭代.
因为它已经明确地限制了*Large*Django QuerySet中的内存使用以及为什么要遍历大量Django QuerySet消耗大量内存?,对django中所有对象的简单迭代将终止机器,因为它将从数据库中检索所有对象.
首先,为了减少你的内存消耗,你应该确保DEBUG是假的(或者修补游标:关闭SQL日志记录,同时保持settings.DEBUG?)以确保django不存储connections调试内容.
但即便如此,
for model in Model.objects.all()
Run Code Online (Sandbox Code Playgroud)
是不行的.
甚至没有稍微改进的形式:
for model in Model.objects.all().iterator()
Run Code Online (Sandbox Code Playgroud)
使用iterator()将通过不在内部存储缓存的结果来节省一些内存(虽然不一定在PostgreSQL上!); 但显然仍会从数据库中检索整个对象.
第一个问题的解决方案是基于计数器对结果进行切片chunk_size.有几种方法可以编写它,但基本上它们都归结为OFFSET + LIMITSQL中的查询.
就像是:
qs = Model.objects.all()
counter = 0
count = qs.count()
while counter < count:
for model in qs[counter:counter+count].iterator()
yield model
counter += chunk_size
Run Code Online (Sandbox Code Playgroud)
虽然这是内存效率(恒定的内存使用量成比例chunk_size),但它在速度方面确实很差:随着OFFSET的增长,MySQL和PostgreSQL(以及可能是大多数数据库)都会开始窒息和放慢速度.
Thierry Schellenbach 在这篇文章中提供了一个更好的解决方案.它过滤PK,这比抵消更快(可能有多快取决于DB)
pk = 0
last_pk = qs.order_by('-pk')[0].pk
queryset = qs.order_by('pk')
while pk < last_pk:
for row in qs.filter(pk__gt=pk)[:chunksize]:
pk = row.pk
yield row
gc.collect()
Run Code Online (Sandbox Code Playgroud)
这开始变得令人满意.现在Memory = O(C),速度〜= O(N)
只有在QuerySet中提供PK时,更好的解决方案才有效.不幸的是,情况并非总是如此,特别是当QuerySet包含distinct(group_by)和/或值(ValueQuerySet)的组合时.
对于那种情况,不能使用"更好的解决方案".
现在我想知道我们是否可以更快地避免关于没有PK的QuerySets的问题.也许使用我在其他答案中找到的东西,但仅限于纯SQL:使用游标.
由于我对原始SQL非常糟糕,特别是在Django中,这里提出了一个真正的问题:
我们如何为大型表构建更好的Django QuerySet迭代器
我从我读过的内容中得知,我们应该使用服务器端游标(显然(参见参考资料)使用标准的Django Cursor不会达到相同的结果,因为默认情况下python-MySQL和psycopg连接器都会缓存结果).
这真的是一个更快(和/或更有效)的解决方案吗?
可以使用django中的原始SQL来完成吗?或者我们应该根据数据库连接器编写特定的python代码?
PostgreSQL和MySQL中的服务器端游标
就目前而言,这是我能得到的......
chunked_iterator()现在,当然最好的方法是将此方法用作django核心或至少是可插拔应用程序的一部分queryset.iterator(),而不是它iterate(queryset).
更新感谢评论中的"T"查找带有一些附加信息的django票.连接器行为的差异使得最好的解决方案可能是创建一个特定的chunked方法而不是透明地扩展iterator(听起来像是一个很好的方法).存在一个实现存根,但是一年中没有任何工作,并且看起来作者还没准备好继续这样做.
Django 1.6正在添加持久数据库连接
在某些情况下,这应该有助于使用游标.仍然超出我目前的技能(以及学习时间)如何实施这样的解决方案..
此外,"更好的解决方案"肯定不适用于所有情况,不能用作通用方法,只能根据具体情况调整存根...
还有另一种选择。它不会使迭代更快(事实上它可能会减慢迭代速度),但它会使其使用更少的内存。根据您的需要,这可能是合适的。
large_qs = MyModel.objects.all().values_list("id", flat=True)
for model_id in large_qs:
model_object = MyModel.objects.get(id=model_id)
# do whatever you need to do with the model here
Run Code Online (Sandbox Code Playgroud)
仅将 id 加载到内存中,并根据需要检索和丢弃对象。请注意增加的数据库负载和较慢的运行时间,这都是减少内存使用的权衡。
我在工作实例上运行异步计划任务时使用了这个,对于这些任务来说,它们是否慢并不重要,但如果它们尝试使用太多内存,它们可能会导致实例崩溃,从而中止进程。
| 归档时间: |
|
| 查看次数: |
3726 次 |
| 最近记录: |