Django मल्टीप्रोसेसिंग और डेटाबेस कनेक्शन


85

पृष्ठभूमि:

मैं एक प्रोजेक्ट पर काम कर रहा हूं जो पोस्टग्रॉज़ डेटाबेस के साथ Django का उपयोग करता है। हम मामले में mod_wsgi का भी उपयोग कर रहे हैं जो मायने रखता है, क्योंकि मेरी कुछ वेब खोजों ने इसका उल्लेख किया है। वेब फ़ॉर्म सबमिट करने पर, Django का दृश्य एक ऐसी नौकरी को छोड़ देता है जिसमें पर्याप्त मात्रा में समय लगेगा (उपयोगकर्ता जितना इंतजार करना चाहेगा), इसलिए हम पृष्ठभूमि में एक सिस्टम कॉल के माध्यम से नौकरी को बंद कर देते हैं। अब जो काम चल रहा है, उसे डेटाबेस में पढ़ने और लिखने में सक्षम होना चाहिए। क्योंकि इस काम में इतना समय लगता है, हम इसके कुछ हिस्सों को समानांतर में चलाने के लिए मल्टीप्रोसेसिंग का उपयोग करते हैं।

मुसीबत:

शीर्ष स्तर की स्क्रिप्ट में डेटाबेस कनेक्शन होता है, और जब यह बाल प्रक्रियाओं को बंद कर देता है, तो ऐसा लगता है कि माता-पिता का कनेक्शन बच्चों के लिए उपलब्ध है। फिर क्वेरी से पहले SET TRANSPORTION ISOLATION LEVEL को कैसे बुलाया जाना चाहिए, इसके बारे में एक अपवाद है। अनुसंधान ने संकेत दिया है कि यह कई प्रक्रियाओं में एक ही डेटाबेस कनेक्शन का उपयोग करने की कोशिश के कारण है। एक थ्रेड मैंने पाया कि चाइल्ड प्रक्रियाओं की शुरुआत में कॉलिंग कनेक्शन। क्लोज () ताकि Django स्वचालित रूप से एक नया कनेक्शन बनाएगा जब उसे एक की आवश्यकता होगी, और इसलिए प्रत्येक बच्चे की प्रक्रिया का एक अनूठा कनेक्शन होगा - अर्थात साझा नहीं किया जाएगा। इसने मेरे लिए काम नहीं किया, क्योंकि चाइल्ड प्रोसेस में कनेक्शन.क्लास () कॉलिंग के कारण माता-पिता की प्रक्रिया यह शिकायत करती है कि कनेक्शन खो गया था।

अन्य खोजें:

कुछ चीजें जो मैंने पढ़ीं, ऐसा प्रतीत होता है कि आप वास्तव में ऐसा नहीं कर सकते हैं, और यह कि मल्टीप्रोसेसिंग, mod_wsgi, और Django अच्छी तरह से एक साथ नहीं खेलते हैं। मुझे लगता है कि विश्वास करना मुश्किल है।

कुछ ने अजवाइन का उपयोग करने का सुझाव दिया, जो एक दीर्घकालिक समाधान हो सकता है, लेकिन मैं इस समय अजवाइन स्थापित करने में असमर्थ हूं, कुछ अनुमोदन प्रक्रियाएं लंबित हैं, इसलिए अभी विकल्प नहीं है।

लगातार डेटाबेस कनेक्शन के बारे में एसओ और अन्य जगहों पर कई संदर्भ मिले, जो मुझे एक अलग समस्या मानते हैं।

इसके अलावा psycopg2.pool और pgpool के संदर्भ और बाउंसर के बारे में कुछ पाया गया। बेशक, मुझे समझ में नहीं आया कि मैं उन पर क्या पढ़ रहा था, लेकिन यह निश्चित रूप से मेरे लिए नहीं था जैसा कि मैं देख रहा था।

वर्तमान "वर्क-अराउंड":

अभी के लिए, मैं केवल धारावाहिक रूप से चलने वाली चीजों पर वापस लौट आया हूं, और यह काम करता है, लेकिन मैं जितना चाहूंगा उससे भी धीमा है।

किसी भी सुझाव के रूप में मैं कैसे समानांतर में चलाने के लिए मल्टीप्रोसेसिंग का उपयोग कर सकता हूं? ऐसा लगता है कि अगर मेरे पास माता-पिता और दो बच्चे हो सकते हैं, तो सभी के डेटाबेस से स्वतंत्र संबंध हैं, चीजें ठीक होंगी, लेकिन मैं उस व्यवहार को प्राप्त नहीं कर सकता।

धन्यवाद, और लंबाई के लिए खेद है!

जवाबों:


71

मल्टीप्रोसेसिंग प्रतिलिपि ऑब्जेक्ट्स को प्रक्रियाओं के बीच जोड़ता है क्योंकि यह प्रक्रियाएँ निकालता है, और इसलिए मूल प्रक्रिया के सभी फ़ाइल विवरणों की प्रतिलिपि बनाता है। कहा जा रहा है, SQL सर्वर का एक कनेक्शन सिर्फ एक फाइल है, आप इसे linux में / proc // fd / .... के तहत देख सकते हैं। किसी भी खुली फाइल को कांटे की प्रक्रियाओं के बीच साझा किया जाएगा। आप यहां फोर्किंग के बारे में और जानकारी प्राप्त कर सकते हैं ।

मेरा समाधान प्रक्रियाओं को लॉन्च करने से ठीक पहले बस डीबी कनेक्शन बंद था, प्रत्येक प्रक्रिया स्वयं कनेक्शन को फिर से बनाती है जब इसे एक की आवश्यकता होगी (django 1.4 में परीक्षण):

from django import db
db.connections.close_all()
def db_worker():      
    some_paralell_code()
Process(target = db_worker,args = ())

मल्टीग्रेसिंग के अर्थ में Pgbouncer / pgpool धागे से नहीं जुड़ा है। यह प्रत्येक अनुरोध पर कनेक्शन को बंद नहीं करने के बजाय समाधान है = उच्च भार के तहत पोस्टग्रेज से कनेक्ट करने में तेजी।

अपडेट करें:

डेटाबेस कनेक्शन के साथ समस्याओं को पूरी तरह से हटाने के लिए, डेटाबेस से जुड़े सभी लॉजिक को db_worker पर ले जाएं - मैं QueryDict को एक तर्क के रूप में पास करना चाहता था ... बेहतर विचार बस आईडी की सूची पास है ... देखें क्वेरी और मान_लिस्ट (आईडी), फ्लैट = देखें सच), और इसे सूची में बदलने के लिए मत भूलना! db_worker के पास जाने से पहले सूची (प्रश्नोत्तर)। इसके लिए धन्यवाद कि हम मॉडल डेटाबेस कनेक्शन की प्रतिलिपि नहीं बनाते हैं।

def db_worker(models_ids):        
    obj = PartModelWorkerClass(model_ids) # here You do Model.objects.filter(id__in = model_ids)
    obj.run()


model_ids = Model.objects.all().values_list('id', flat=True)
model_ids = list(model_ids) # cast to list
process_count = 5
delta = (len(model_ids) / process_count) + 1

# do all the db stuff here ...

# here you can close db connection
from django import db
db.connections.close_all()

for it in range(0:process_count):
    Process(target = db_worker,args = (model_ids[it*delta:(it+1)*delta]))   

क्या आप उस बिट को आईडी से गुजरने के बारे में एक क्वेरी से लेकर स्व-उत्तर वाले प्रश्न तक समझा सकते हैं?
झारवुड

1
मल्टीप्रोसेसिंग प्रतियां कनेक्शन ऑब्जेक्ट्स को प्रक्रियाओं के बीच ले जाती हैं क्योंकि यह प्रक्रियाओं की तलाश करती है, और इसलिए मूल प्रक्रिया के सभी फ़ाइल विवरणों की प्रतिलिपि बनाती है। कहा जा रहा है कि, mysql सर्वर का एक कनेक्शन सिर्फ एक फ़ाइल है, आप इसे linux में / proc / <PID> / fd / .... के तहत देख सकते हैं। किसी भी खुली फ़ाइल को forked प्रक्रियाओं AFAIK के बीच साझा किया जाएगा। stackoverflow.com/questions/4277289/…
vlad-ardelean

1
क्या यह धागे पर भी लागू होता है? उदाहरण के लिए। मुख्य थ्रेड में करीब db कोन, फिर प्रत्येक थ्रेड में db को एक्सेस करें, क्या प्रत्येक थ्रेड को अपना कनेक्शन मिलेगा?
जेम्स लिन

1
आपको django.db.connections.close_all()एक कॉल के साथ सभी कनेक्शन बंद करने के लिए उपयोग करना चाहिए ।
डेनिस मालिनोवस्की

1
हम्म ... यहाँ django के लोगों के बीच काफी दिलचस्प बातचीत है: code.djangoproject.com/ticket/20562 शायद इस विषय पर कुछ प्रकाश डाला जाए? मूल रूप से कनेक्शन 'फोर्केबल नहीं हैं' ... प्रत्येक प्रक्रिया का अपना कनेक्शन होना चाहिए।
लेचप

18

कई डेटाबेस का उपयोग करते समय, आपको सभी कनेक्शन बंद करना चाहिए।

from django import db
for connection_name in db.connections.databases:
    db.connections[connection_name].close()

संपादित करें

कृपया सभी कनेक्शनों को बंद करने के लिए @lechup के समान उपयोग करें (निश्चित नहीं है कि कब से django संस्करण इस विधि को जोड़ा गया है):

from django import db
db.connections.close_all()

9
यह सिर्फ db.close_connection को कई बार कॉल कर रहा है
ibz

2
मैं यह नहीं देख सकता कि कैसे यह कहीं भी उपनाम या जानकारी का उपयोग किए बिना काम कर सकता है।
रेमकोगर्लिच

यह ... काम नहीं कर सकता। @Mounir, आप इसे उपयोग करने के लिए संशोधित करना चाहिए aliasया infoमें forपाश शरीर, अगर dbया close_connection()समर्थन करता है।
0atman

5

पायथन 3 और Django 1.9 के लिए यह वही है जो मेरे लिए काम कर रहा है:

import multiprocessing
import django
django.setup() # Must call setup

def db_worker():
    for name, info in django.db.connections.databases.items(): # Close the DB connections
        django.db.connection.close()
    # Execute parallel code here

if __name__ == '__main__':
    multiprocessing.Process(target=db_worker)

ध्यान दें कि django.setup () के बिना मुझे यह काम करने के लिए नहीं मिल सकता है। मैं अनुमान लगा रहा हूं कि मल्टीप्रोसेसिंग के लिए कुछ को फिर से शुरू करने की आवश्यकता है।


धन्यवाद! इसने मेरे लिए काम किया और शायद django के नए संस्करणों के लिए अब स्वीकृत उत्तर होना चाहिए।
कृष्ण

Django तरीका प्रबंधन कमांड बनाना है जो स्टैंडअलोन आवरण स्क्रिप्ट नहीं बनाता है। यदि आप प्रबंधन आदेश का उपयोग नहीं करते हैं तो आपको setupdjango का उपयोग करने की आवश्यकता है ।
लेचप जूल

2
लूप के लिए आपका वास्तव में कुछ भी नहीं कर रहा है db.connections.databases.items()- यह सिर्फ कनेक्शन को कई बार बंद कर रहा है। db.connections.close_all()जब तक इसे कार्यकर्ता फ़ंक्शन कहा जाता है तब तक ठीक काम करता है।
ताओ_ओट

2

Django परीक्षण मामलों को क्रमिक रूप से चलाने पर मेरे पास "बंद कनेक्शन" मुद्दे थे । परीक्षणों के अलावा, परीक्षण निष्पादन के दौरान डेटाबेस को जानबूझकर संशोधित करने की एक और प्रक्रिया भी है। यह प्रक्रिया प्रत्येक टेस्ट केस सेटअप () में शुरू की गई है।

TransactionTestCaseइसके बजाय मेरी परीक्षा कक्षाओं को इनहेरिट करने के लिए एक साधारण फिक्स था TestCase। यह सुनिश्चित करता है कि डेटाबेस वास्तव में लिखा गया था, और अन्य प्रक्रिया में डेटा पर अप-टू-डेट दृश्य है।


1

(एक महान समाधान नहीं है, लेकिन एक संभव समाधान)

यदि आप अजवाइन का उपयोग नहीं कर सकते हैं, तो शायद आप अपनी स्वयं की कतार प्रणाली को लागू कर सकते हैं, मूल रूप से कुछ टास्क टेबल में कार्य जोड़ सकते हैं और एक नियमित क्रोन हो सकता है जो उन्हें बंद कर देता है और प्रक्रिया करता है? (एक प्रबंधन आदेश के माध्यम से)


संभवतः - जटिलता के उस स्तर से बचने की उम्मीद कर रहा था, लेकिन अगर इसका एकमात्र समाधान है, तो मुझे उस सड़क से नीचे जाना पड़ सकता है - सुझाव के लिए धन्यवाद। अजवाइन सबसे अच्छा जवाब है? यदि हां, तो मैं इसे प्राप्त करने के लिए धक्का देने में सक्षम हो सकता हूं, लेकिन इसमें थोड़ा समय लगेगा। मैं एक मशीन पर समानांतर प्रसंस्करण के विपरीत वितरित प्रसंस्करण के साथ अजवाइन को
जोड़ता

2
अजवाइन अनुरोध-प्रतिक्रिया चक्र के बाहर आवश्यक किसी भी प्रसंस्करण के लिए एक अच्छा फिट है
दूसरा 13

1

अरे मैं इस मुद्दे में भाग गया और निम्नलिखित प्रदर्शन करके इसे हल करने में सक्षम था (हम एक सीमित कार्य प्रणाली को लागू कर रहे हैं)

task.py

from django.db import connection

def as_task(fn):
    """  this is a decorator that handles task duties, like setting up loggers, reporting on status...etc """ 
    connection.close()  #  this is where i kill the database connection VERY IMPORTANT
    # This will force django to open a new unique connection, since on linux at least
    # Connections do not fare well when forked 
    #...etc

ScheduledJob.py

from django.db import connection

def run_task(request, job_id):
    """ Just a simple view that when hit with a specific job id kicks of said job """ 
    # your logic goes here
    # ...
    processor = multiprocessing.Queue()
    multiprocessing.Process(
        target=call_command,  # all of our tasks are setup as management commands in django
        args=[
            job_info.management_command,
        ],
        kwargs= {
            'web_processor': processor,
        }.items() + vars(options).items()).start()

result = processor.get(timeout=10)  # wait to get a response on a successful init
# Result is a tuple of [TRUE|FALSE,<ErrorMessage>]
if not result[0]:
    raise Exception(result[1])
else:
   # THE VERY VERY IMPORTANT PART HERE, notice that up to this point we haven't touched the db again, but now we absolutely have to call connection.close()
   connection.close()
   # we do some database accessing here to get the most recently updated job id in the database

ईमानदारी से, दौड़ की स्थितियों (कई एक साथ उपयोगकर्ताओं के साथ) को रोकने के लिए आप डेटाबेस को कॉल करना सबसे अच्छा होगा। प्रक्रिया के बाद जितनी जल्दी हो सके। अभी भी एक मौका हो सकता है कि लाइन से नीचे एक और उपयोगकर्ता पूरी तरह से डीबी के लिए अनुरोध करता है इससे पहले कि आपके पास डेटाबेस को फ्लश करने का मौका हो।

सभी ईमानदारी में यह सुरक्षित होगा और आपके कांटे को कमांड को सीधे कॉल न करने के लिए अधिक स्मार्ट होने की आवश्यकता होगी , लेकिन इसके बजाय ऑपरेटिंग सिस्टम पर एक स्क्रिप्ट को कॉल करें ताकि स्पॉन्स्ड कार्य अपने स्वयं के django शेल में चले!


मैंने पहले के बजाय कांटा के अंदर बंद करने के अपने विचार का इस्तेमाल किया, एक डेकोरेटर बनाने के लिए जिसे मैं अपने कार्यकर्ता कार्यों में जोड़ता हूं।
रीब

1

आप Postgre को अधिक संसाधन दे सकते हैं, Debian / Ubuntu में आप संपादित कर सकते हैं:

nano /etc/postgresql/9.4/main/postgresql.conf

अपने पोस्टग्रे संस्करण द्वारा 9.4 की जगह।

यहां कुछ उपयोगी पंक्तियां दी गई हैं, जिन्हें उदाहरण के साथ ऐसा करने के लिए अद्यतन किया जाना चाहिए, नाम खुद के लिए बोलते हैं:

max_connections=100
shared_buffers = 3000MB
temp_buffers = 800MB
effective_io_concurrency = 300
max_worker_processes = 80

सावधान रहें कि इन मापदंडों को बहुत अधिक बढ़ावा न दें क्योंकि यह पोस्टग्रे के साथ त्रुटियों को जन्म दे सकता है जो उपलब्ध से अधिक पुन: स्रोत लेने की कोशिश कर रहा है। ऊपर के उदाहरण 4 कोर के साथ सुसज्जित डेबियन 8 जीबी रैम मशीन पर ठीक चल रहे हैं।


0

अगर आपको केवल I / O समानता की आवश्यकता है और समानता की प्रक्रिया नहीं है, तो आप अपनी प्रक्रियाओं को थ्रेड्स पर स्विच करके इस समस्या से बच सकते हैं। बदलने के

from multiprocessing import Process

साथ में

from threading import Thread

Threadवस्तु के रूप में एक ही इंटरफ़ेस हैProcsess


0

यदि आप कनेक्शन पूलिंग का भी उपयोग कर रहे हैं, तो निम्नलिखित ने हमारे लिए काम किया, कांटे होने के बाद कनेक्शन को जबरन बंद करना। इससे पहले मदद के लिए नहीं लग रहा था।

from django.db import connections
from django.db.utils import DEFAULT_DB_ALIAS

connections[DEFAULT_DB_ALIAS].dispose()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.