手动 Post 保存信号创建使应用程序变慢,Django

Manual Post Save Signal creation makes the application slow, Django

我们有一个使用 Django-river 进行工作流管理的 Django 应用程序。为了提高性能,我们不得不使用 bulk_create。我们需要将数据插入到几个表中,每个表中有几行。 最初,我们使用的是普通的 .save() 方法,并且工作流按预期工作(因为 post save() 信号正在正确创建)。但是一旦我们移动到 bulk_create,性能就会从几分钟提高到几秒钟。但是 Django_river 停止工作并且没有默认的 post 保存信号。我们必须根据可用的文档来实现信号。

class CustomManager(models.Manager):
    def bulk_create(items,....):
         super().bulk_create(...)
         for i in items:
              [......] # code to send signal

class Task(models.Model):
    objects = CustomManager()
    ....

这使工作流再次运行,但信号的生成需要时间,这会破坏 bulk_create 获得的所有性能改进。 那么有没有办法改善信号的产生?

更多详情

def post_save_fn(obj):
    post_save.send(obj.__class__, instance=obj, created=True) 

class CustomManager(models.Manager):
    def bulk_create(self, objs, **kwargs):
        #Your code here
        data_obj = super(CustomManager, self).bulk_create(objs,**kwargs)
        for i in data_obj:
            # t1 = threading.Thread(target=post_save_fn, args=(i,))
            # t1.start()
            post_save.send(i.__class__, instance=i, created=True) 
        return data_obj
        
        
class Test(Base): 
    test_name = models.CharField(max_length=100)
    test_code = models.CharField(max_length=50)
    objects = CustomManager()
    class Meta:
        db_table = "test_db"

有什么问题?

正如其他人在评论中提到的那样,问题是通过 post_save 调用的函数需要很长时间。 (请记住,信号不是异步的!!-这是一个常见的误解)。

我不熟悉 django-river 但快速浏览一下将被调用的函数 post-save(参见 here and here)我们可以看到它们涉及额外的调用数据库。

虽然您通过使用 bulk_create 保存了大量单独的数据库命中,但您仍在为每个 post_save 信号再次多次调用数据库。

可以做些什么?

简而言之。不多!!对于绝大多数 django 请求,缓慢的部分将调用数据库。这就是为什么我们尝试尽量减少对数据库的调用次数(使用 bulk_create)。

通读 django-river 的前几段,整个想法是将通常在代码中的内容移动到数据库中。这里的一大优势是您不需要经常重新编写代码和重新部署。但缺点是您将不可避免地不得不更多地引用数据库,这会减慢速度。这对于某些用例来说没问题,但不是全部。

我能想到的两件事可能会有所帮助:

  • 所有这些当前是否都作为 request/response 周期的一部分发生。如果是,是否需要?如果这两个问题的答案分别是 'yes' 和 'no',那么您可以将此工作移至单独的任务队列。这仍然会很慢,但至少不会减慢您的网站速度。
  • 具体取决于您的工作流程和您正在创建的数据的性质,可能您可以执行 post_save 指示的所有操作正在做你自己的职能,并且做得更有效率。但这肯定取决于您的数据和您的应用程序,并且会偏离 django-river.
  • 的理念

如果“信号”逻辑允许您在批量保存后执行,请使用单独的工作人员。

您可以创建一个额外的队列 table 并放置有关为您未来的工作人员做什么的元数据。

使用队列中所需的逻辑和数据创建一个单独的工作程序(Django 模块)table。您可以将其作为管理命令执行,这将允许您 运行 主流程中的工作人员(您可以 运行 来自常规 Django 代码的管理命令)或者您可以 运行 通过 crontab根据时间表。

如何运行这样的工人?

如果您需要像创建记录一样紧密地完成某些事情 - 运行 使用 threading 模块在单独的线程中完成。因此,您的请求-响应生命周期将在您启动新线程后立即完成。

否则,如果您可以稍后再做 - 制定一个计划,然后 运行 使用管理命令框架通过 crontab 进行计划。