25
# 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()
  • セロリはpを漬けて労働者に送りますよね?
  • ワーカーが複数のマシンで実行されている場合、personオブジェクト全体(主に必要とされないかさばるtext_blobとともに)がネットワーク経由で送信されますか?それを回避する方法はありますか?
  • 複数のマシンで実行されているワーカーにPersonレコードを効率的かつ均等に配布するにはどうすればよいですか?

  • これはより良いアイデアでしょうか?Personが数百万のレコードを持っている場合、それはデータベースを圧倒しませんか?

    # 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()
    
4

2 に答える 2

18

モデル オブジェクト全体を渡すよりも、PK を渡す方が適切で安全だと思います。PK は単なる数字であるため、シリアル化も非常に簡単です。最も重要なことは、より安全なサリアライザー (pickle の代わりに json/yaml) を使用できるため、モデルのシリアル化に問題がないという安心感を得ることができるということです。

この記事が言うように

Celery は分散システムであるため、タスクがどのプロセスで実行されるのか、さらにはどのマシンで実行されるのかさえわかりません。そのため、Django モデル オブジェクトを引数としてタスクに渡すべきではありません。競合状態が発生する可能性があるため、ほとんどの場合、代わりにデータベースからオブジェクトを再取得することをお勧めします。

于 2014-05-24T16:28:16.913 に答える
2

はい。データベースに何百万ものレコードがある場合、これはおそらく最良のアプローチではありませんが、何百万ものレコードをすべて処理する必要があるため、何をしても、DB はかなり打撃を受けます。難しい。

ここにいくつかの代替案がありますが、どれも「より良い」とは言えませんが、ただ違うだけです。

  1. .title() 処理を行う Person クラスの pre_save シグナル ハンドラーを実装します。そうすれば、first_name/last_names は常にデータベースに正しく保存され、これを再度行う必要はありません。
  2. ある種のページング パラメータを使用する管理コマンドを使用します。おそらく、姓の最初の文字を使用して個人をセグメント化します。したがって、実行./manage.py my_task aすると、姓が「a」で始まるすべてのレコードが更新されます。明らかに、データベース全体を取得するには、これを数回実行する必要があります
  3. 多分あなたはいくつかの創造的なSQLでそれを行うことができます. ここで試すつもりはありませんが、調査する価値があるかもしれません。

.save() は、実際に何百万ものレコードを選択するよりも、データベースへの「ヒット」が難しくなることに注意してください。

于 2013-02-26T00:36:10.750 に答える