手动 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 进行计划。
我们有一个使用 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 进行计划。