可以在 django 中异步执行数据库操作吗?
Posted
技术标签:
【中文标题】可以在 django 中异步执行数据库操作吗?【英文标题】:It's possible to perform db operations asynchronously in django? 【发布时间】:2021-01-07 14:55:32 【问题描述】:我正在编写一个在数据库中随机创建 500 万个订单的命令。
def constrained_sum_sample(
number_of_integers: int, total: Optional[int] = 5000000
) -> int:
"""Return a randomly chosen list of n positive integers summing to total.
Args:
number_of_integers (int): The number of integers;
total (Optional[int]): The total sum. Defaults to 5000000.
Yields:
(int): The integers whose the sum is equals to total.
"""
dividers = sorted(sample(range(1, total), number_of_integers - 1))
for i, j in zip(dividers + [total], [0] + dividers):
yield i - j
def create_orders():
customers = Customer.objects.all()
number_of_customers = Customer.objects.count()
for customer, number_of_orders in zip(
customers,
constrained_sum_sample(number_of_integers=number_of_customers),
):
for _ in range(number_of_orders):
create_order(customer=customer)
number_of_customers
将至少大于 1k,create_order
函数至少执行 5 次 db 操作(一个用于创建订单,一个用于随机获取订单的商店,一个用于创建订单项目(这可以最多 30 个,也是随机的),一个用于获取项目的产品(或更高但等于该项目),一个用于创建销售说明。
您可能会怀疑这需要很长时间才能完成。我尝试过异步执行这些操作,但没有成功。我的所有尝试(至少十几个;其中大多数使用sync_to_async
)都引发了以下错误:
SynchronousOnlyOperation you cannot call this from an async context - use a thread or sync_to_async
在我继续破脑袋之前,我问:有没有可能实现我的愿望?如果是这样,我应该如何进行?
非常感谢!
【问题讨论】:
您是否可以同时运行多个异步方法,但在其中您有await sync_to_async(write_to_db)(**props_as_primitives)
?
对不起,我不明白你的问题。我的问题其实很简单:我想让create_orders
函数的第一个循环异步。
首先,您正在创建 5M 行,这肯定需要时间(也取决于您的数据库的高可扩展性)。其次,调用async
db connection 可能对您没有帮助,因为它们必须按 "sequential" 顺序进行(简单示例:您必须在创建订单之前创建客户实例)。
我宁愿在尝试移植到 async 解决方案之前尝试清理我的代码。
@JPG 您可以从客户已经创建的代码中看到。此外,订单不需要按“顺序”顺序排列。最后,简单地说“清理你的代码”实际上有助于零。
【参考方案1】:
尚不支持,但正在开发中。
Django 3.1 正式支持视图和中间件的异步,但是如果您尝试在异步函数中调用 ORM,您将获得 SynchronousOnlyOperation。
如果您需要从异步函数调用 DB,他们提供了帮助工具,例如: async_to_sync 和 sync_to_async 在线程或协程模式之间切换如下:
from asgiref.sync import sync_to_async
results = await sync_to_async(Blog.objects.get, thread_sensitive=True)(pk=123)
如果您需要对 DB 的调用进行排队,我们曾经使用 celery 或 rabbitMQ 等任务队列。
顺便说一句,如果你真的知道你在做什么,你可以打电话给它,但你自己负责 只需关闭异步安全,但要注意数据丢失和完整性错误#settings.py
DJANGO_ALLOW_ASYNC_UNSAFE=True
在 Django 中需要这样做的原因是许多库,特别是数据库适配器,要求在创建它们的同一线程中访问它们。此外,许多现有的 Django 代码假定它们都在同一个线程中运行,例如中间件将内容添加到请求中以供以后在视图中使用。
发行说明中有更多有趣的新闻: https://docs.djangoproject.com/en/3.1/topics/async/
【讨论】:
【参考方案2】:实现你的愿望是有可能的,但是你需要一个不同的视角来解决这个问题。
尝试使用异步工作器,一个简单的方法是 rq workers 或 celery。
使用这些库之一来处理在不同线程或进程中定义在 django 中的异步长时间运行任务。
【讨论】:
【参考方案3】:你可以使用bulk_create()来创建大量对象,这样会加快进程,另外把bulk_create()放在一个单独的线程下。
【讨论】:
是的 bulk_create 会帮助但不能解决问题。我怎样才能把它放在一个单独的线程中?以上是关于可以在 django 中异步执行数据库操作吗?的主要内容,如果未能解决你的问题,请参考以下文章
Handsontable beforeRemoveRow 可以异步吗?