django模型对象实例应该传递给芹菜吗?

时间:2013-02-26 00:26:30

标签: python django celery

# models.py
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    text_blob = models.CharField(max_length=50000)

# tasks.py
import celery
@celery.task
def my_task(person):
    # example operation: does something to person 
    # needs only a few of the attributes of person
    # and not the entire bulky record
    person.first_name = person.first_name.title()
    person.last_name = person.last_name.title()
    person.save()

在我的应用程序某处我有类似的东西:

from models import Person
from tasks import my_task
import celery
g = celery.group([my_task.s(p) for p in Person.objects.all()])
g.apply_async()
  • 芹菜泡菜将它发送给工人吗?
  • 如果工作人员在多台计算机上运行,​​是否会通过网络传输整个人对象(以及主要不需要的庞大text_blob)?有没有办法避免它?
  • 如何有效且均匀地将人员记录分发给在多台计算机上运行的员工?

  • 这可能是个更好的主意吗?如果Person有几百万条记录,难道不会压倒db吗?

    # tasks.py
    
    import celery
    from models import Person
    @celery.task
    def my_task(person_pk):
        # example operation that does not need text_blob
        person = Person.objects.get(pk=person_pk)
        person.first_name = person.first_name.title()
        person.last_name = person.last_name.title()
        person.save()
    
    
    #In my application somewhere
    from models import Person
    from tasks import my_task
    import celery
    g = celery.group([my_task.s(p.pk) for p in Person.objects.all()])
    g.apply_async()
    

2 个答案:

答案 0 :(得分:12)

我认为通过PK而不是整个模型对象更好更安全。由于PK只是一个数字,序列化也更简单。最重要的是,您可以使用更安全的sarializer(json / yaml而不是pickle),并且可以放心,您在序列化模型时不会遇到任何问题。

正如this文章所说:

  

由于Celery是一个分布式系统,因此您无法知道在哪个进程中运行,甚至无法知道任务运行在哪台机器上。因此,您不应该将Django模型对象作为任务的参数传递,而是从数据库中重新获取对象几乎总是更好,因为可能存在竞争条件。

答案 1 :(得分:1)

是。如果数据库中有数百万条记录,那么这可能不是最好的方法,但是因为你必须经历所有数百万条记录,那么无论你做什么,你的数据库都会受到很大影响。硬。

以下是一些替代方案,其中没有一个我称之为“更好”,只是不同。

  1. 为您的Person类实现一个pre_save信号处理程序,该处理程序执行.title()。这样你的first_name / last_names将始终在数据库中正确存储,你不必再这样做了。
  2. 使用带有某种分页参数的管理命令...也许使用姓氏的第一个字母来分割人员。因此,运行./manage.py my_task a将更新姓氏以“a”开头的所有记录。显然你必须多次运行才能通过整个数据库
  3. 也许你可以用一些创意的sql来做。我甚至不打算在这里尝试,但可能值得调查。
  4. 请记住,.save()将更难“击中”数据库,然后实际选择数百万条记录。