首页 > 解决方案 > 可以在 django 中异步执行数据库操作吗?

问题描述

我正在编写一个命令来在数据库中随机创建 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

在我继续破脑袋之前,我问:有没有可能实现我的愿望?如果是这样,我应该如何进行?

非常感谢!

标签: djangoasynchronouspython-asyncio

解决方案


尚不支持,但正在开发中。

Django 3.1 正式支持视图和中间件的异步,但是如果您尝试在异步函数中调用 ORM,您将获得 SynchronousOnlyOperation。

如果您需要从 async 函数调用 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/


推荐阅读