在 Django 中聚合 save()s?
Posted
技术标签:
【中文标题】在 Django 中聚合 save()s?【英文标题】:Aggregating save()s in Django? 【发布时间】:2011-03-24 15:21:51 【问题描述】:我正在使用带有 sqlite 后端的 Django,写入性能是个问题。我可能会在某个阶段毕业到一个“合适的”数据库,但目前我被 sqlite 困住了。我认为我的写入性能问题可能与我正在创建大量行的事实有关,并且大概每次我save()
时都会锁定、解锁和同步磁盘上的数据库。
如何将大量 save()
调用聚合到单个数据库操作中?
【问题讨论】:
***.com/questions/1136106/… @Tomasz - 感谢您的链接,@commit_manually 装饰器为我提供了所需的性能改进。我已将我的问题标记为欺骗,因为我认为另一个问题很好地涵盖了它。 您实际上可以结束您的问题,而不是简单地投票支持结束,你知道的。 使用参数化查询也快得多。见***.com/questions/904796/… 【参考方案1】:已编辑:commit_on_success
已被弃用并在 Django 1.8 中被删除。请改用transaction.atomic
。请参阅 Fraser Harris 的answer。
实际上,这比您想象的要容易。你可以在 Django 中使用transactions。这些批处理数据库操作(特别是保存、插入和删除)成为一个操作。我发现最容易使用的是commit_on_success
。本质上,您将数据库保存操作包装到一个函数中,然后使用 commit_on_success
装饰器。
from django.db.transaction import commit_on_success
@commit_on_success
def lot_of_saves(queryset):
for item in queryset:
modify_item(item)
item.save()
这将有巨大的速度提升。如果任何项目失败,您还将获得回滚的好处。如果您有数百万个保存操作,那么您可能必须使用 commit_manually
和 transaction.commit()
将它们分块提交,但我很少需要这样做。
希望对你有帮助,
会
【讨论】:
这简直太神奇了。我包装了一个创建大量记录的随机函数,在 SQLite 中它立即变得快了好几倍。 对于那些现在遇到此答案的人,commit_on_success
现在已弃用。取而代之的是atomic
,这是新版本,会有同样的神奇效果!
atomic
参考:docs.djangoproject.com/en/dev/topics/db/transactions/…
这绝对是白天制作的,批量插入快了大约 30 倍。真是神奇的效果!!【参考方案2】:
Django 1.6 的新功能是atomic, a simple API to control DB transactions。从文档中逐字复制:
atomic 可用作decorator:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
作为context manager:
from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django's default).
do_stuff()
with transaction.atomic():
# This code executes inside a transaction.
do_more_stuff()
旧版 django.db.transaction
函数 autocommit()
、commit_on_success()
和 commit_manually()
已被弃用,并将在 Django 1.8 中删除。
【讨论】:
【参考方案3】:我认为这是您正在寻找的方法:https://docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create
从文档中复制的代码:
Entry.objects.bulk_create([
Entry(headline='This is a test'),
Entry(headline='This is only a test'),
])
在实践中,看起来像:
my_entries = list()
for i in range(100):
my_entries.append(Entry(headline='Headline #'+str(i))
Entry.objects.bulk_create(my_entries)
根据文档,这将执行单个查询,而不管列表的大小(SQLite3 上最多 999 个项目),这对于 atomic
装饰器来说是不能说的。
有一个重要的区别。听起来,从 OP 的问题来看,他试图批量创建而不是批量保存。 atomic
装饰器是保存最快的解决方案,但不是创建。
【讨论】:
我的一项工作遵循类似的方向。没有主键的模型有办法解决吗?我正在使用字典创建模型对象。没有 pk 我得到异常“dict object has no attribute pk”。【参考方案4】:“如何将大量的 save() 调用聚合到单个数据库操作中?”
你不需要。 Django 已经为你管理了一个缓存。你不能通过尝试保存来改进它的数据库缓存。
“写入性能问题可能与我创建大量行有关”
正确。
SQLite 很慢。它就是这样儿的。查询比大多数其他数据库更快。写入速度很慢。
考虑更严重的架构更改。您是否在网络事务期间加载行(即批量上传文件并从这些文件加载数据库)?
如果您在网络事务中进行批量加载,请停止。你需要做一些更聪明的事情。使用celery 或使用其他一些“批处理”工具在后台进行加载。
我们尝试将自己限制在网络事务中的文件验证,并在用户不等待他们的 html 页面时进行加载。
【讨论】:
虽然可以公平地指出,由于它使用磁盘的方式,sqlite 确实在写入方面确实很慢,但它不必这个慢:正如建议的那样作为评论员,我开始使用 @commit_manually 装饰器,发现确实有很大的改进。 SQLite3 由于其文件锁定机制而运行缓慢。锁定和解锁文件以进行写访问需要时间。因此,将所有保存都放在与transaction.atomic()
的事务中将解决您的问题,因为许多save()
调用发生在单个文件锁定/文件解锁周期中。以上是关于在 Django 中聚合 save()s?的主要内容,如果未能解决你的问题,请参考以下文章
python,django,向mysql更新数据时save()报错不能用