Manual Post Save Signal的创建会让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,但快速浏览一下保存后调用的函数(见这里和这里),我们可以看到它们涉及到对数据库的额外调用。

虽然您通过使用bulk_create节省了很多单独的db命中,但您仍然要为每个postrongave信号多次调用数据库。

有什么办法呢?

简而言之。不多! !对于绝大多数的django请求,缓慢的将调用数据库的一部分。这就是为什么我们尝试最小化对db的调用数量(使用bulk_create之类的东西)。

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

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

  • 当前所有这些是否作为请求/响应周期的一部分发生?如果是,那还需要吗?如果这两个问题的答案分别是"是"one_answers"否",那么您可以将这项工作移到单独的任务队列中。这仍然会很慢,但至少它不会减慢你的网站。
  • 根据你的工作流程和你正在创建的数据的性质,它可能在这种情况下,你可以做post_save信号在你自己的函数中所做的一切,并且做得更有效。但这肯定取决于你的数据,你的应用程序,并将偏离django-river的哲学。

使用一个分离的worker逻辑允许你在批量保存后执行。

你可以创建一个额外的队列表,并放置关于为未来的工人做什么的元数据。

从队列表中创建一个独立的worker (Django模块),包含所需的逻辑和数据。你可以把它作为管理命令,这将允许你在主流程中运行worker(你可以从普通的Django代码中运行管理命令),或者你可以根据调度通过crontab来运行它。

如何运行这样一个worker?

如果你需要做的事情,因为你已经创建了记录-运行它在一个单独的线程使用threading模块。所以你的请求-响应生命周期将在你启动一个新线程后完成。

如果你可以以后再做,那就制定一个时间表,并使用管理命令框架通过crontab运行它。

相关内容

最新更新