programing

MySQLdb를 사용하여 커서를 닫아야 하는 경우

prostudy 2022. 10. 3. 16:08
반응형

MySQLdb를 사용하여 커서를 닫아야 하는 경우

WSGI 웹 앱을 만들고 있으며 MySQL 데이터베이스를 가지고 있습니다.문장을 실행하고 결과를 얻기 위한 커서를 제공하는 MySQLdb를 사용하고 있습니다.커서를 가져오고 닫는 표준 방법은 무엇입니까?특히 커서의 지속 시간은 어느 정도입니까?거래할 때마다 커서를 새로 받아야 하나요?

커밋하기 전에 커서를 닫아야 한다고 생각합니다.중간 커밋이 필요 없는 트랜잭션 세트를 검색하여 각 트랜잭션에 대해 새로운 커서를 얻을 필요가 없는 중요한 이점이 있습니까?새 커서를 얻는 데 많은 비용이 드나요, 아니면 큰 문제가 되지 않나요?

표준 프랙티스가 무엇인지 묻는 대신 모듈 자체의 지침을 찾는 것이 좋습니다.with다른 사용자가 제안한 키워드는 좋은 아이디어이지만, 이 특정 상황에서는 원하는 기능을 제공하지 못할 수 있습니다.

1.2에서는 1.2.5로 설정되어 있습니다.MySQLdb.Connection는 다음 코드(github)를 사용하여 컨텍스트 매니저 프로토콜을 구현합니다.

def __enter__(self):
    if self.get_autocommit():
        self.query("BEGIN")
    return self.cursor()

def __exit__(self, exc, value, tb):
    if exc:
        self.rollback()
    else:
        self.commit()

&A에 관한 Q는 여러 .withPython의 "with" 스테이트먼트를 읽어보실 수 있습니다만, 기본적으로는 다음과 같습니다.__enter__를 시작할 때 실행됩니다.with및 "block", "block", "block",__exit__를 종료하면 실행된다.with 블록을 할 수 . 옵션 구문을 사용할 수 있습니다.with EXPR as VAR __enter__나중에 해당 개체를 참조할 경우 이름을 지정합니다.위의 구현을 통해 데이터베이스를 쿼리하는 간단한 방법을 다음에 제시하겠습니다.

connection = MySQLdb.connect(...)
with connection as cursor:            # connection.__enter__ executes at this line
    cursor.execute('select 1;')
    result = cursor.fetchall()        # connection.__exit__ executes after this line
print result                          # prints "((1L,),)"

는 접속 with 그 블록이요?__exit__에서는, 콜의 「」만 표시됩니다.self.rollback() ★★★★★★★★★★★★★★★★★」self.commit() 모두 '이러한 방법'으로 불리지는 않습니다'라고 는 않습니다close() 커서가 없습니다.__exit__정의되어 해도 , 이 이 정의되어 있기 때문입니다.설령 정의되어 있어도 상관없습니다.with는 접속 관리만 하고 있습니다.따라서.with위의 예에 다음 코드를 추가하면 쉽게 확인할 수 있습니다.

try:
    cursor.execute('select 1;')
    print 'cursor is open;',
except MySQLdb.ProgrammingError:
    print 'cursor is closed;',
if connection.open:
    print 'connection is open'
else:
    print 'connection is closed'

출력 "cursor is open, connection is open" 이 stdout 으로 출력됩니다.

커밋하기 전에 커서를 닫아야 한다고 생각합니다.

왜냐고요? MySQL C API를 기반으로 합니다.MySQLdb모듈 설명서에서 "MySQL은 커서를 지원하지 않지만 커서는 쉽게 에뮬레이트할있습니다."라고 암시한 바와 같이,는 커서 개체를 구현하지 않습니다.정말이지,MySQLdb.cursors.BaseCursorobject커밋/커밋과 관련하여 커서에 이러한 제한을 가하지 않습니다.Oracle 개발자는 다음과 같이 말했습니다.

cur.commit()보다 앞의 cnx.commit()가 더 논리적으로 들립니다.아마도 당신은 "더 이상 필요하지 않으면 커서를 닫아라"라는 규칙으로 갈 수 있을 것이다.따라서 커서를 닫기 전에 commit()를 지정합니다.결국 커넥터/Python의 경우 큰 차이는 없지만 다른 데이터베이스도 마찬가지입니다.

이 주제에 대한 "표준 관행"에 근접할 것으로 예상합니다.

중간 커밋이 필요 없는 트랜잭션 세트를 검색하여 각 트랜잭션에 대해 새로운 커서를 얻을 필요가 없는 중요한 이점이 있습니까?

매우 의심스럽지만, 그렇게 하려고 하면 또 다른 인적 오류가 발생할 수 있습니다.규약을 정하고 그것을 고수하는 것이 좋다.

새 커서를 얻는 데 많은 비용이 드나요, 아니면 큰 문제가 되지 않나요?

오버헤드는 무시할 수 있고 데이터베이스 서버에는 전혀 영향을 주지 않습니다.이것은 완전히 MySQLdb의 실장 내에 있습니다.새로운 커서를 작성했을 때 무슨 일이 일어나는지 궁금하다면 github에서 확인할 수 있습니다.

아까 얘기하던 때로 돌아가서withMySQLdb.Connection ★★★__enter__ ★★★★★★★★★★★★★★★★★」__exit__ 새 합니다.with따라서 블록의 끝을 추적하거나 닫을 필요가 없습니다.이것은 꽤 가볍고 순전히 당신의 편의를 위해 존재한다.

커서 오브젝트를 세밀하게 관리하는 것이 그렇게 중요한 경우 contextlib을 사용할 수 있습니다.커서 객체가 정의되지 않은 사실을 보완하기 위해 닫기__exit__경우, 접속 때, 접속 오브젝트를 사용하여 접속 오브젝트를 강제 도 있습니다.with "closed; closed : block 이 됩니다. "my_my_my_my_my_my_my_my_my_my_mym

from contextlib import closing
import MySQLdb

with closing(MySQLdb.connect(...)) as my_conn:
    with closing(my_conn.cursor()) as my_curs:
        my_curs.execute('select 1;')
        result = my_curs.fetchall()
try:
    my_curs.execute('select 1;')
    print 'my_curs is open;',
except MySQLdb.ProgrammingError:
    print 'my_curs is closed;',
if my_conn.open:
    print 'my_conn is open'
else:
    print 'my_conn is closed'

:with closing(arg_obj)인수 의 인수가 되지 않습니다.__enter__ ★★★★★★★★★★★★★★★★★」__exit__메서드. 인수 객체의 메서드만 호출합니다.closewith ( 블록을 하십시오. (이것이 동작하고 있는 것을 확인하려면 , 간단하게 클래스를 정의해 주세요.Foo__enter__,__exit__ , , , , 입니다.close한 「」을 .print 일이 일어나는지 비교해보세요.with Foo(): passwith closing(Foo()): pass 다음의 의 중요한 있습니다. 여기에는 다음 두 가지 중요한 의미가 있습니다.

첫 번째로 는 MySQLdb가 .BEGIN(「」를 사용하고 경우)with connection블록의 마지막에 트랜잭션을 커밋 또는 롤백합니다.이는 MySQLdb의 기본 동작으로, 모든 DML 문을 즉시 커밋하는 MySQL의 기본 동작으로부터 사용자를 보호합니다.매니저를 할 때인 MySQLdb를 합니다.BEGIN서버의 자동 커밋 설정을 바이패스합니다. if ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」with connectionautocommit이 실제로는 바이패스되어 있을 때만 디세블로 되어 있다고 생각할 수 있습니다. '하다'를 붙이면 수 있습니다.closing변경 내용을 롤백할 수 없고 동시성 버그가 나타나기 시작하며 그 이유를 바로 알 수 없습니다.

번째, ㅇㅇㅇ,with closing(MySQLdb.connect(user, pass)) as VAR접속 오브젝트를 에 바인드합니다.VAR 「」는 「」입니다.with MySQLdb.connect(user, pass) as VAR커서 오브젝트를 에 바인드합니다.VAR후자의 경우 연결 객체에 직접 액세스할 수 없습니다.의 ""를 .connection 에 프록시 액세스를 합니다.가 닫힙니다.connection는 Attribute로 됩니다.None이로 인해 다음 중 하나가 발생할 때까지 접속이 포기됩니다.

  • 커서에 대한 모든 참조가 제거됩니다.
  • 커서가 범위를 벗어납니다.
  • 접속이 타임아웃 되었습니다.
  • 서버 관리 도구를 통해 연결이 수동으로 닫힙니다.

이를 테스트하려면 다음 행을 하나씩 실행하면서 열린 연결을 모니터링합니다(Workbench 또는 를 사용).

with MySQLdb.connect(...) as my_curs:
    pass
my_curs.close()
my_curs.connection          # None
my_curs.connection.close()  # throws AttributeError, but connection still open
del my_curs                 # connection will close here

'with' 키워드를 사용하여 다시 쓰는 것이 좋습니다.'With'는 자동으로 커서를 닫는 데 주의를 기울입니다(관리되지 않는 리소스이므로 중요).장점은 예외의 경우에도 커서를 닫을 수 있다는 것입니다.

from contextlib import closing
import MySQLdb

''' At the beginning you open a DB connection. Particular moment when
  you open connection depends from your approach:
  - it can be inside the same function where you work with cursors
  - in the class constructor
  - etc
'''
db = MySQLdb.connect("host", "user", "pass", "database")
with closing(db.cursor()) as cur:
    cur.execute("somestuff")
    results = cur.fetchall()
    # do stuff with results

    cur.execute("insert operation")
    # call commit if you do INSERT, UPDATE or DELETE operations
    db.commit()

    cur.execute("someotherstuff")
    results2 = cur.fetchone()
    # do stuff with results2

# at some point when you decided that you do not need
# the open connection anymore you close it
db.close()

주의: 이 답변은 MySQLdb의 드롭인 대체품이자 MySQLdb의 유지보수가 중단된 이후 사실상 최신 버전의 MySQLdb에 대한 답변입니다.여기 있는 모든 것이 레거시 MySQLdb에 대해서도 맞는다고 생각합니다만, 아직 확인하지 않았습니다.

우선 몇 가지 사실:

  • Python의 구문은 컨텍스트 매니저의 구문을 호출합니다.__enter__with그 「」을 참조해 주세요.__exit__방법을 지정합니다.
  • 연결에는 커서 생성 및 반환 외에 아무것도 하지 않는 메서드와 커밋 또는 롤백(예외가 느려졌는지 여부에 따라 다름)하는 메서드가 있습니다.접속은 닫히지 않습니다.
  • PyMy 커서SQL은 순전히 Python에서 구현된 추상화이며 MySQL 1자체에는 이와 동등한 개념이 없습니다.
  • 커서는 아무것도 하지 않는 메서드와 커서를 "닫는" 메서드가 있습니다(즉, 상위 연결에 대한 커서의 참조를 무효화하고 커서에 저장된 데이터를 폐기하는 것을 의미합니다).
  • 커서는 생성된 연결에 대한 참조를 유지하지만 연결은 생성한 커서에 대한 참조를 유지하지 않습니다.
  • 연결에는 닫는 메서드가 있습니다.
  • https://docs.python.org/3/reference/datamodel.html,에 따르면 CPython(기본 Python 구현)은 참조 카운트를 사용하여 참조 수가 0이 되면 개체를 자동으로 삭제합니다.

이것들을 종합하면, 다음과 같은 순진한 코드는 이론적으로 문제가 있다는 것을 알 수 있습니다.

# Problematic code, at least in theory!
import pymysql
with pymysql.connect() as cursor:
    cursor.execute('SELECT 1')

# ... happily carry on and do something unrelated

문제는 어떤 것도 연결을 닫지 않았다는 것입니다. Python을 실행하면 Python을 실행할 수 .SHOW FULL PROCESSLISTMySQL 쉘에서 생성한 유휴 연결을 볼 수 있습니다.MySQL의 기본 연결 수는 151개많지 않기 때문에, 이러한 연결을 열어두는 프로세스가 많으면 이론적으로 문제가 발생하기 시작할 수 있습니다.

단, CPython에서는 의 예시와 같은 코드가 열려 있는 접속의 로드에 영향을 주지 않도록 하는 장점이 있습니다.그 유일한 장점은 그 순간cursor를 들어 됨) 또는 "완료됨"(예: "완료됨")cursor는 ) 0에 참조 카운트가 카운트가 0으로 떨어집니다.그러면 0으로 됩니다).__del__이치노 쉘에 Python을 실행하여 을 수행할 수 .cursor = 'arbitrary value'하면, 에서 .; 、 [ ] 、 [ ] 、 [ ] 、 [ ] 、 [ ] 。SHOW PROCESSLIST★★★★★★★★★★★★★★★★★★.

그러나, 이것에 의존하는 것은 부적절하며, 이론적으로는 CPython 이외의 Python 실장에서는 실패할 수 있습니다.'청정자는 '명시적으로' ' 말하면 '청결'이다..close()connection(Python이 개체를 파괴할 때까지 기다리지 않고 데이터베이스에서 연결을 해제합니다. 보다

import contextlib
import pymysql
with contextlib.closing(pymysql.connect()) as conn:
    with conn as cursor:
        cursor.execute('SELECT 1')

이것은 보기 흉하지만 Python이 오브젝트를 파괴하여 데이터베이스 접속을 해방하는 것에 의존하지 않습니다.

이미 이렇게 명시적으로 연결을 닫는 경우 커서를 닫는 것은 전혀 의미가 없습니다.

마지막으로, 여기서 두 번째 질문에 답합니다.

새 커서를 얻는 데 많은 비용이 드나요, 아니면 큰 문제가 되지 않나요?

아니요, 커서를 인스턴스화해도 MySQL에 전혀 영향을 주지 않고 기본적으로 아무 것도 하지 않습니다.

중간 커밋이 필요 없는 트랜잭션 세트를 검색하여 각 트랜잭션에 대해 새로운 커서를 얻을 필요가 없는 중요한 이점이 있습니까?

이것은 상황적이어서 일반적인 대답을 하기 어렵다.https://dev.mysql.com/doc/refman/en/optimizing-innodb-transaction-management.html의 표현대로 "어플리케이션이 초당 수천 커밋하면 성능 문제가 발생할 수 있으며 2~3시간마다 커밋하면 다른 성능 문제가 발생할 수 있습니다."모든 커밋에 대해 퍼포먼스 오버헤드를 지불하지만 트랜잭션을 장시간 열어두면 다른 접속이 잠금을 기다리는 데 시간을 할애할 가능성이 높아지고 교착 상태가 발생할 위험이 높아지며 다른 접속에 의해 실행되는 검색 비용이 증가할 수 있습니다.


1 MySQL에는 커서라고 불리는 구조가 있지만 저장 프로시저 내에만 존재합니다.PyMy와는 완전히 다릅니다.SQL 커서 및 여기서는 관련이 없습니다.

모든 실행에 하나의 커서를 사용하고 코드 끝에 커서를 닫는 것이 좋을 것 같습니다.이 제품은 사용하기 쉽고 효율성 이점도 있을 수 있습니다(그 점에 대해서는 언급하지 마십시오).

conn = MySQLdb.connect("host","user","pass","database")
cursor = conn.cursor()
cursor.execute("somestuff")
results = cursor.fetchall()
..do stuff with results
cursor.execute("someotherstuff")
results2 = cursor.fetchall()
..do stuff with results2
cursor.close()

요점은 커서 실행 결과를 다른 변수에 저장하여 커서를 해방하여 두 번째 실행을 할 수 있다는 것입니다.이 방법으로 문제가 발생하는 것은 fetchone()을 사용하여 첫 번째 쿼리의 모든 결과를 반복하기 전에 두 번째 커서를 실행해야 하는 경우뿐입니다.

그렇지 않으면 모든 데이터를 꺼내는 즉시 커서를 닫으십시오.그래야 코드 뒷부분을 묶을 걱정을 하지 않아도 됩니다.

및 mysqlphp mysql처럼 .첫 번째 데이터를 인쇄하기 전에 코드 시작 부분에서 i를 시작합니다. 접속 했을 경우, 「」라고 됩니다.50x(내부 에러가 기억나지 않는다) 에러 메세지가 표시됩니다.그리고 세션 전체를 열어두고 더 이상 필요하지 않을 때 닫으십시오.

언급URL : https://stackoverflow.com/questions/5669878/when-to-close-cursors-using-mysqldb

반응형