使用多处理池更新Django模型会锁定数据库

时间:2018-12-02 08:45:19

标签: python django postgresql multiprocessing jupyter-notebook

我使用Jupyter Notebook来处理存储在django / postgres中的数据。我以这种方式初始化我的项目:

sys.path.append('/srv/gr/prg')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'prg.settings')
if 'setup' in dir(django):
    django.setup()

有许多更新数据的单独进程,我想对其进行多线程处理以加快进程。当我在单个线程中进行更新或使用sqlite时,一切工作都很好。

def extract_org_description(id):
    o = models.Organization.objects.get(pk=id)
    logging.info("Looking for description for %s" % o.symbol)
    try:
        content = open('/srv/data/%s.html' % o.symbol)
    except FileNotFoundError:
        logging.error("HTML file not found for %s" % o.symbol)
        return
    doc = BeautifulSoup(content, 'html.parser')
    desc = doc.select("#cr_description_mod > div.cr_expandBox > div.cr_description_full.cr_expand")
    if not desc or not desc[0]:
        logging.info("Cannot not find description for %s" % o.symbol)
        return
    o.description = desc[0].text
    o.save(update_fields=['description'])
    logging.info("Description for %s found" % o.symbol)
    return("done %s" % id)

这将不起作用:

p = Pool(2)
result = p.map(extract_org_description, orgs)
print(result)

在大多数情况下,它会挂起,直到我中断它为止,而没有任何特定错误,有时postgres将出现“已有事务正在进行中”,有时我看到“没有要提取的结果”错误。试着使用池的大小,我可以使其工作一次或两次,但是很难诊断出到底是什么问题。

我尝试更改策略以选择对象并将其映射到将对象作为参数的extract_org_description(与基于键选择不同),但这并没有更好的效果。

我唯一想到的是,当django尝试自动提交时,所有单独的更新(包括在其他线程中发生的更新)都在同一事务范围内,这引起了问题。但是我不知道如何在Django中解决这个问题。

1 个答案:

答案 0 :(得分:1)

您的问题包括 multiprocessing multithread 术语,但重要的是要了解它们是实现并发的不同方法。

Django具有对多线程的内置支持,并且将create a new database connection for each thread。如果从多处理切换到多线程,则应该解决问题。

在多处理中,整个过程是分叉的,新过程将具有与旧过程相同的数据库连接。这就导致了您所看到的问题,例如,当另一个进程已经在同一数据库连接上打开一个新事务时,您尝试打开一个新事务。

如果您确实需要多处理而非多线程,则可能有解决方案。例如,this answer建议仅关闭数据库连接,迫使Django创建新的连接。