我有一个有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()”再次)。
答案 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_domains
和create_domain_ids
列表。
请确保您拥有适当的域名索引。来自get_or_create docs:
这个方法是原子假设正确使用,正确的数据库 配置和底层数据库的正确行为。 但是,如果未在数据库级别强制执行唯一性 在get_or_create调用中使用的kwargs(请参阅unique或unique_together), 这种方法容易出现竞争条件,可能导致多重竞争 同时插入具有相同参数的行。