Как обрабатывать базу данных многопроцессорной обработки Python concurrency, в частности, с django?
Итак, я пытаюсь написать приложение, которое использует django как свой ORM, так как ему нужно будет сделать некоторые за кулисами и простой в использовании интерфейс. Это основная функциональность будет обрабатывать данные, которые в базе данных, в высоко-процессорном процессе (в основном monte carlo simulations), и я хочу реализовать многопроцессорность, в частности, используя 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
С кодом как таковым я получаю прерывистый DatabaseError
или PicklingError
s. Первые обычно имеют форму "искаженная база данных" или "потерянное соединение с сервером MySQL", последние обычно "не могут рассортировать model.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 мс, он начнет действовать. Итак, вероятно проблема concurrency, не так ли? Но только с 4 процессами. Мой вопрос заключается в том, как решить эту проблему, не дав больших дампов данных раньше времени? Я протестировал его как с SQLite, так и с MySQL, и у обоих есть проблемы с этим.
Ответы
Ответ 1
Хорошо, поэтому я решил (с помощью друга), что проблема в том, что django использует одно и то же соединение с базой данных для всех процессов. Обычно, когда у вас есть параллельные запросы db, они либо находятся в одном потоке (в этом случае GIL запускается), либо они находятся в отдельных потоках, и в этом случае django создает разные соединения с базой данных. Но при многопроцессорности питон делает глубокие копии всего, поэтому он передает одно и то же соединение с базами данных с подпроцессами, а затем они наступают друг на друга, пока он не сломается.
Решение должно инициировать новое соединение db изнутри каждого подпроцесса (что относительно быстро).
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 многопроцессорности и подключения к базе данных
...
и просто вызовите операцию закрытия соединения в подпроцессах:
from django.db import connection
connection.close()