Python 라이브러리 모듈에서 데이터베이스 연결을 처리하는 방법


23

데이터베이스에 액세스하기위한 함수가 포함 된 Python으로 라이브러리를 만들었습니다. 타사 응용 프로그램이 적절한 API를 제공하지 않기 때문에 작성된 타사 응용 프로그램 데이터베이스 주위의 래퍼 라이브러리입니다. 이제 프로그램 논리가 특정 함수를 몇 천 번 호출 할 함수에 대한 중첩 호출을 사용할 때까지 원래는 함수 호출 기간 동안 각 함수가 데이터베이스 연결을 열도록했습니다. 이것은 성능이 좋지 않았습니다. 이를 프로파일 링하면 오버 헤드가 데이터베이스 연결 설정에 있었으며 함수 호출 당 한 번만 발생했습니다. 따라서 라이브러리 모듈을 가져올 때 데이터베이스 연결이 열리도록 열린 연결을 함수 내에서 모듈 자체로 옮겼습니다. 이것은 나에게 허용 가능한 성능을 주었다.

이제 이것에 관한 두 가지 질문이 있습니다. 먼저, 더 이상 데이터베이스 연결을 명시 적으로 종료하지 않으며이 설정을 사용하여 어떻게 명시 적으로 수행 할 수 있는지 걱정해야합니까? 둘째, 내가 한 일이 모범 관행과 가까운 곳 어디에서나 떨어지고 어떻게 다른 방법으로 접근 할 수 있습니까?


1
openConn함수를 제공 하고 사용자가 호출하는 각 함수에 함수를 전달하도록합니다. 이렇게하면 with명령문 등으로 연결 범위를 지정할 수 있습니다.
Daniel Gratzer

1
jozfeg에 동의하고 생성자 내에서 DB 연결을 열고 종료시 연결을 닫는 클래스를 만드는 것을 고려하십시오.
Nick Burns

답변:


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()

이렇게하면 시작시 데이터베이스 연결이 인스턴스화되고 개체가 인스턴스화 된 위치가 범위를 벗어나면 닫힙니다. 참고 : 객체를 모듈 수준에서 인스턴스화 하면 전체 응용 프로그램에 대해 유지됩니다. 이것이 의도되지 않는 한 데이터베이스 기능을 비 데이터베이스 기능과 분리하는 것이 좋습니다.

운 좋게도 파이썬은 데이터베이스 API표준화 했으므로 모든 호환 DB와 호환됩니다. :)


그걸 어떻게 피합니까 self에서 def query(self,?
samayo

2
피하십시오 정의? 자체는 이것을 클래스 메소드가 아닌 인스턴스 메소드로 정의합니다. 클래스에서 정적 속성으로 데이터베이스를 만든 다음 클래스 메서드 만 사용할 수 있지만 (어디서나 필요하지 않음) 데이터베이스는 개별 인스턴스화가 아닌 클래스에 전역 적입니다.
트래비스

예, 간단한 쿼리를하기 위해 귀하의 예를 사용하려고 시도했기 때문에 db.query('SELECT ...', var)세 번째 인수가 필요하다는 불만이있었습니다.
samayo

@samson, MyDB먼저 객체 를 인스턴스화해야합니다 :db = MyDB(); db.query('select...', var)
cowbert

이 메시지를 방지ResourceWarning: unclosed <socket.socket...
밥 스타 인

3

데이터베이스 연결을 처리하는 동안 고려해야 할 두 가지 사항이 있습니다.

  1. 다중 연결 인스턴스화 방지, 각 함수가 데이터베이스 연결을 열 수 있도록하는 것은 나쁜 습관으로 간주되어 제한된 수의 데이터베이스 세션을 제공하므로 세션이 부족합니다. 적어도 솔루션이 확장되지 않고 싱글 톤 패턴을 사용하면 클래스가 한 번만 인스턴스화됩니다.이 패턴에 대한 자세한 내용은 링크를 참조 하십시오

  2. 앱 종료시 연결을 닫을 때, 그렇지 않았으며 적어도 12 개의 앱 인스턴스가 동일하게 실행되고 있다고 가정 해 봅시다. 처음에는 모든 것이 잘 될 것이지만 데이터베이스 세션이 부족하고 유일한 해결책입니다. 데이터베이스 서버를 다시 시작하는 것입니다. 라이브 앱에는 좋지 않으므로 가능한 한 항상 동일한 연결을 사용하십시오.

이러한 모든 개념을 강화하기 위해 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이고 어떻게 해결할 수 있는지 이해할 수 없습니다 .
Ilya Rusin

1
내 잘못 인 @IlyaRusin 사실 데이터베이스는 Postgres뿐만 아니라 다른 RDBMS 처리에 공통적 인 메소드를 넣는 상위 클래스 일뿐입니다. 그러나 실수로 죄송하지만 수정 된 버전이 도움이되기를 바랍니다. 관련 질문이 있으시면 언제든지 추가하고, 필요에 맞게 코드를 수정하십시오.
ponach

반복적 Postgres.query(Postgres(), some_sql_query)으로 while루프를 호출하는 경우 각 반복에서 여전히 연결을 열고 닫 while습니까, 아니면 프로그램이 끝날 때까지 루프 전체 시간 동안 계속 열어 두 겠습니까?

연결 클래스가 싱글로 구현 @Michael, 따라서 그것은 단지 하나의 시간을 인스턴스화 할 것이지만, 전반적인 내가 대신 변수에 시작, 호출의 제안 방법에 대해 추천 할 것입니다
ponach

1
@ponach 고마워, 내가 달성하고 싶었던 것과 정확히 일치합니다. 코드를 약간 수정하고 query()함수 에서 UPDATE 문을 사용하려고 시도했지만 "병렬"로 앱을 실행할 때 코드에 문제가있는 것 같습니다. 별도의 질문을했습니다 : 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 문을 사용하여 클래스를 호출하여 데이터베이스에 대한 연결을 자동으로 닫는 편리한 방법을 제공합니다.

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

-1

이것에 대해 생각하는 데 오랜 시간이 걸렸습니다. 오늘, 나는 길을 찾았다. 나는 그것이 최선의 방법인지 모른다. 이름이 conn.py 인 파일을 작성하여 /usr/local/lib/python3.5/site-packages/conn/ 폴더에 저장하십시오. 나는 freebsd를 사용하는데 이것이 내 사이트 패키지 폴더의 경로입니다. 내 conn.py에서 : conn = "dbname = omnivore user = postgres password = 12345678"

```````````````````````````````````````````````````` 스크립트에서 connection을 호출하고 싶습니다.

수입 psycopg2 수입 psycopg2.extras 수입 psycopg2.extensions

conn import에서 conn 시도 : conn = psycopg2.connect (conn.conn) 다음을 제외하고 : page = "데이터베이스에 액세스 할 수 없습니다"

cur = conn.cursor ()

그리고 blah blah ....

나는 이것이 유용하기를 바랍니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.