पायथन लाइब्रेरी मॉड्यूल में डेटाबेस कनेक्शन से कैसे निपटें


23

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

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


1
एक openConnफ़ंक्शन प्रदान करें और उपयोगकर्ता को उनके द्वारा कॉल किए जाने वाले प्रत्येक फ़ंक्शन को पास करें, इस तरह वे एक withबयान में कनेक्शन को गुंजाइश कर सकते हैं या जो भी
डैनियल ग्रैज़र

1
मैं जॉज़फेग से सहमत हूं, एक ऐसा वर्ग बनाने पर विचार करता हूं जो कंस्ट्रक्टर के भीतर db कनेक्शन को खोलता है, और जो बाहर निकलने पर कनेक्शन को बंद कर देता है
निक बर्न्स

जवाबों:


31

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

हालाँकि, यह मामला नहीं हो सकता है! मैं सुझाव दूंगा, जैसा कि दूसरों की टिप्पणियों में है, इसे किसी वस्तु में लपेटने के लिए।

class MyDB(object):

    def __init__(self):
        self._db_connection = db_module.connect('host', 'user', 'password', 'db')
        self._db_cur = self._db_connection.cursor()

    def query(self, query, params):
        return self._db_cur.execute(query, params)

    def __del__(self):
        self._db_connection.close()

यह शुरुआत में आपके डेटाबेस कनेक्शन को तुरंत चालू कर देगा, और उस स्थान को बंद कर देगा, जब आपकी वस्तु को जिस स्थान पर रखा गया था, वह कार्यक्षेत्र से बाहर हो जाता है। नोट: यदि आप मॉड्यूल स्तर पर इस ऑब्जेक्ट को त्वरित करते हैं , तो यह आपके संपूर्ण अनुप्रयोग के लिए बना रहेगा। जब तक यह इरादा न हो, मैं आपके डेटाबेस फ़ंक्शंस को ग़ैर-डेटाबेस फ़ंक्शंस से अलग करने का सुझाव दूंगा।

सौभाग्य से, अजगर ने डेटाबेस एपीआई को मानकीकृत किया है , इसलिए यह आपके लिए सभी आज्ञाकारी DB के साथ काम करेगा :)


आप कैसे है कि से बचने के है selfमें def query(self,?
समोयो

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

हाँ, क्योंकि मैंने आपके उदाहरण का उपयोग करने के लिए एक सरल क्वेरी बनाने की कोशिश की db.query('SELECT ...', var)और इसने तीसरे तर्क की आवश्यकता के बारे में शिकायत की।
23

@ सैमसन, आपको MyDBसबसे पहले ऑब्जेक्ट को तत्काल करना होगा:db = MyDB(); db.query('select...', var)
काउबर्ड

यह संदेश को रोकता हैResourceWarning: unclosed <socket.socket...
बॉब स्टेन

3

डेटाबेस कनेक्शन को संभालते समय दो बातों का ध्यान रखना चाहिए:

  1. कई कनेक्शन तात्कालिकता को रोकना, प्रत्येक फ़ंक्शन को डेटाबेस कनेक्शन को खोलने देना बुरा व्यवहार माना जाता है, सीमित संख्या में डेटाबेस सत्र देने से आप सत्रों से बाहर हो जाएंगे; कम से कम आपके समाधान से बाहर नहीं निकलेगा, इसके बजाय, सिंगलटन पैटर्न का उपयोग करें, आपकी कक्षा केवल एक बार त्वरित रूप से प्राप्त होगी, इस पैटर्न के बारे में अधिक जानकारी के लिए: लिंक

  2. ऐप से बाहर निकलने पर कनेक्शन बंद करना, मान लें कि आपने नहीं किया है, और आपके पास ऐप के कम से कम दर्जन उदाहरण हैं जो पहले ही कर रहे हैं, पहले तो सब कुछ ठीक हो जाएगा, लेकिन आप डेटाबेस सत्र से बाहर निकल जाएंगे, और एकमात्र ठीक हो जाएगा डेटाबेस सर्वर को पुनरारंभ करना होगा, जो कि लाइव ऐप के लिए अच्छी बात नहीं है इसलिए जब भी संभव हो उसी कनेक्शन का उपयोग करें।

इन सभी अवधारणाओं को ठोस बनाने के लिए निम्न उदाहरण देखें जो psycopg2 को लपेटता है

import psycopg2


class Postgres(object):
"""docstring for Postgres"""
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
            # normally the db_credenials would be fetched from a config file or the enviroment
            # meaning shouldn't be hardcoded as follow
            db_config = {'dbname': 'demo', 'host': 'localhost',
                     'password': 'postgres', 'port': 5432, 'user': 'postgres'}
            try:
                print('connecting to PostgreSQL database...')
                connection = Postgres._instance.connection = psycopg2.connect(**db_config)
                cursor = Postgres._instance.cursor = connection.cursor()
                cursor.execute('SELECT VERSION()')
                db_version = cursor.fetchone()

            except Exception as error:
                print('Error: connection not established {}'.format(error))
                Postgres._instance = None

            else:
                print('connection established\n{}'.format(db_version[0]))

        return cls._instance

    def __init__(self):
        self.connection = self._instance.connection
        self.cursor = self._instance.cursor

    def query(self, query):
        try:
            result = self.cursor.execute(query)
        except Exception as error:
            print('error execting query "{}", error: {}'.format(query, error))
            return None
        else:
            return result

    def __del__(self):
        self.connection.close()
        self.cursor.close()

1
नमस्ते! आपके उत्तर के लिए धन्यवाद। लेकिन जब मेरे पास मेरे मामले में इसे लागू करने की कोशिश होती है if Database._instance is None: NameError: name 'Database' is not defined। मैं समझ नहीं पा रहा हूं कि Databaseयह क्या है और मैं इसे कैसे ठीक कर सकता हूं।
इल्या रुसिन

1
@IlyaRusin जो मेरी गलती थी, वास्तव में डेटाबेस सिर्फ एक अभिभावक वर्ग है जिसमें मैं अलग-अलग RDBMS हैंडलिंग के लिए सामान्य तरीके रखता हूं क्योंकि मैं न केवल पोस्टग्रेज से कनेक्ट करता हूं। हालाँकि, गलती के लिए क्षमा करें और मुझे आशा है कि सही किया गया संस्करण आपके लिए उपयोगी हो सकता है, यदि आप किसी भी संबंधित प्रश्न में संकोच नहीं करते हैं, तो अपनी आवश्यकताओं के लिए कोड को संशोधित करने के लिए स्वतंत्र महसूस करें।
ponach

अगर मैं बार-बार Postgres.query(Postgres(), some_sql_query)एक whileलूप में कॉल करूंगा, तो क्या यह अभी भी प्रत्येक पुनरावृत्ति में कनेक्शन को खोलेगा और बंद करेगा, या whileप्रोग्राम के बाहर निकलने तक लूप के पूरे समय तक इसे खुला रखेगा ?

@ मिचेल को कनेक्शन वर्ग एक सिंगलटन के रूप में कार्यान्वित किया जाता है, इसलिए इसे केवल एक समय में त्वरित किया जाएगा, लेकिन कुल मिलाकर मैं कॉल के सुझाए गए तरीके के खिलाफ सिफारिश करूंगा, इसके बजाय इसे एक चर में शुरू
करूं

1
@ponach धन्यवाद, यह वही करता है जो मैं हासिल करना चाहता था। मैंने आपके कोड को थोड़ा सा अनुकूलित किया और आपके query()फ़ंक्शन में एक अद्यतन कथन का उपयोग करने की कोशिश की , लेकिन ऐसा लगता है कि मेरे कोड के साथ कोई समस्या है जब मैं "समानांतर" में अपना ऐप चलाता हूं। मैंने इसके बारे में एक अलग सवाल किया: softwareengineering.stackexchange.com/questions/399582/…

2

अपनी वस्तुओं के लिए संदर्भ प्रबंधक क्षमताओं की पेशकश करना दिलचस्प होगा। इसका मतलब है कि आप इस तरह से एक कोड लिख सकते हैं:

class MyClass:
    def __init__(self):
       # connect to DB
    def __enter__(self):
       return self
    def __exit__(self):
       # close the connection

यह आपको डेटाबेस को स्टेटमेंट को बंद करने के लिए एक आसान तरीका पेश करेगा जिसमें स्टेटमेंट के साथ क्लास को कॉल करके स्वचालित रूप से कॉल किया जा सकता है:

with MyClass() as my_class:
   # do what you need
# at this point, the connection is safely closed.

-1

इस बारे में सोचने के लिए एक लंबा समय। दिन तक, मुझे रास्ता मिल गया। मुझे नहीं पता कि यह सबसे अच्छा तरीका है। आप नाम के साथ एक फ़ाइल बनाते हैं: con.py, और इसे /usr/local/lib/python3.5/site-packages/conn/ फ़ोल्डर में सहेजें। मैं freebsd का उपयोग करता हूं और यह मेरी साइट-संकुल फ़ोल्डर का पथ है। in my con.py: con = "dbname = omnivore user = postgres password = 12345678"

`` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` `` और स्क्रिप्ट में मैं कनेक्शन कॉल करना चाहता हूं, मैं लिखता हूं:

आयात psycopg2 आयात psycopg2.extras आयात psycopg2.extensions

कॉन इम्पोर्ट कॉन ट्राई से: con = psycopg2.connect (conn.conn) को छोड़कर: पेज = "डेटाबेस को एक्सेस नहीं कर सकता है"

cur = conn.cursor ()

और ब्ला ब्ला

मुझे आशा है कि यह उपयोगी है

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