【问题标题】:How to handle Python multiprocessing database concurrency, specifically with django?如何处理 Python 多处理数据库并发,特别是使用 django?
【发布时间】:2013-08-18 03:28:22
【问题描述】:

所以,我正在尝试编写一个使用 django 作为其 ORM 的应用程序,因为它既需要做一些幕后处理,又需要一个易于使用的前端。它的核心功能是处理数据库中的数据,在高 CPU 进程(基本上是蒙特卡罗模拟)中,我想实现多处理,特别是使用 Pool(我得到 4 个进程)。基本上我的代码是这样运行的,有大约 20 个父母的孩子:

assorted import statements to get the django environment in the script
from multiprocessing import Pool
from random import random
from time import sleep

def test(child):
    x=[]
    print child.id
    for i in range(100):
        print child.id, i
        x.append(child.parent.id) #just to hit the DB
    return x

if __name__ == '__main__':
    parent = Parent.objects.get(id=1)
    pool = Pool()
    results = []
    results = pool.map(test,parent.children.all())
    pool.close()
    pool.join()
    print results

使用这样的代码,我得到间歇性的DatabaseErrors 或PicklingErrors。前者通常是“格式错误的数据库”或“丢失与 MySQL 服务器的连接”的形式,后者通常是“无法腌制模型.DoesNotExist”。它们是随机的,发生在任何进程中,当然数据库本身没有任何问题。如果我设置 pool = Pool(proccesses=1) 然后它运行,在一个线程中就好了。我还加入了各种打印语句,以确保它们中的大多数实际上都在运行。

我也一直在将test 更改为:

def test(child):
    x=[]
    s= random()
    sleep(random())
    for i in range(100):
        x.append(child.parent.id)
    return x

这只会使每次迭代在运行前暂停不到一秒,并且一切都很好。如果我将随机间隔降低到大约 500 毫秒,它就会开始起作用。所以,可能是并发问题,对吧?但只有 4 个进程命中。我的问题是如何在不提前大量转储数据的情况下解决这个问题?我已经用 SQLite 和 MySQL 对其进行了测试,但都遇到了这个问题。

【问题讨论】:

  • 既然进程是 CPU 密集型的,那你为什么不使用multiprocessing.Lock 来避免数据库的所有竞争条件?

标签: python sql django multiprocessing pool


【解决方案1】:

好的,所以我确定(在朋友的帮助下)问题在于 django 对所有进程使用相同的数据库连接。通常,当您有并发 db 请求时,它们要么在同一个线程中(在这种情况下 GIL 启动),要么它们在不同的线程上,在这种情况下,django 会建立不同的数据库连接。但是对于多处理,python 会对所有内容进行深度复制,因此它会将相同的数据库连接传递给子进程,然后它们会相互踩踏直到它中断。

解决方案是从每个子进程中触发一个新的数据库连接(相对较快)。

from django import db
...
def sub_process():
    db.close_connection()
    #the rest of the sub_process' routines

#code that calls sub_process with the pool

从有那条线到没有那条线来回走动,肯定能解决所有问题。

【讨论】:

  • 谢谢!我之前使用过线程,但在开始使用多处理时遇到了问题。你对这两个案例的解释让我很开心。
  • 如果在多处理调用之前和之后有数据库调用,这将失败。
【解决方案2】:

其实我最近也遇到了同样的问题,看看这个帖子:Django multiprocessing and database connections ... 并在子进程中调用连接关闭操作:

from django.db import connection 
connection.close()

【讨论】:

    猜你喜欢
    • 2011-05-24
    • 2018-10-03
    • 1970-01-01
    • 2016-03-14
    • 1970-01-01
    • 2023-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多