Celery / django重复违规

时间:2016-05-29 22:07:49

标签: django celery

我有一个有5个线程的芹菜工人。它正在通过django的ORM抓取网站并将域名保存到数据库。

以下是它的大致情况:

domain_all = list(Domain.objects.all())
needs_domain = set()

for x in dup_free_scrape:
    domain = x['domain']
    if any(domain.lower() == s.name.lower() for s in domain_all):
        x['domainn'] = [o for o in domain_all if domain.lower() == o.name.lower()][0]
    else:
        print('adding: {}'.format(domain))
        needs_domain.add(domain)

create_domains = [Domain(name=b.lower()) for b in needs_domain]
create_domains_ids = Domain.objects.bulk_create(create_domains)

可能不是最好的方法,但它会针对数据库中已有的所有域检查一个字典(dup_free_scrape)中的域。

在遇到错误之前,它可能会超过数百甚至数千,但有时会这样做:

  

任务keywords.domains.model_work [285c3e74-8e47-4925-9ab6-a99540a24665]   意外提升:IntegrityError('重复键值违反唯一   约束“keywords_domain_name_key”\ nDETAIL:键   (name)=(domain.com)已经存在。\ n',)   django.db.utils.IntegrityError:重复键值违反唯一   约束“keywords_domain_name_key”

我能想到的这个问题的唯一原因是:一个线程将域保存到DB而另一个线程在上面的代码中间?

我找不到任何好的解决方案,但这里有想法(不确定是否有任何好处):在事务中包装整个事件,如果数据库引发错误简化重试(查询数据库为“Domain.objects.all()”再次)。

1 个答案:

答案 0 :(得分:2)

如果您正在批量创建这些记录并且有多个线程,那么IntegrityError很可能是由插入相同数据的不同线程引起的。你真的需要多个线程吗?如果是,你可以尝试:

create_domains = []
create_domain_ids = []

for x in dup_free_scrape:
    domain = x['domain']
    new_domain, created = Domain.objects.get_or_create(name = domain.lower()
    if created:
        create_domains.append(domain.lower())
        created_domain_ids.append(new_domain.pk)

请注意,这是所有代码。不需要在开始时选择的所有查询。 Domain.objects.all()效率非常低,因为你正在那里阅读整个表格。

另请注意,您对x['domain']的列表理解似乎完全是多余的。

除非您想要跟踪正在创建的内容,否则可能不需要

create_domainscreate_domain_ids列表。

请确保您拥有适当的域名索引。来自get_or_create docs:

  

这个方法是原子假设正确使用,正确的数据库   配置和底层数据库的正确行为。   但是,如果未在数据库级别强制执行唯一性   在get_or_create调用中使用的kwargs(请参阅unique或unique_together),   这种方法容易出现竞争条件,可能导致多重竞争   同时插入具有相同参数的行。