source

MySQL 클라이언트를 MySQLDB로 자동 재연결하는 방법은?

ittop 2023. 10. 24. 21:35
반응형

MySQL 클라이언트를 MySQLDB로 자동 재연결하는 방법은?

PHP를 사용한 방법을 우연히 발견했습니다.

my_bool reconnect = 1;
mysql_options(&mysql, MYSQL_OPT_RECONNECT, &reconnect);

MySQLdb(python-mysql)에서는 운이 없습니다.

아무도 단서를 줄 수 있습니까?감사해요.

나는 이 문제를 해결하기 위해 감싼 기능을 만들었습니다.cursor.execute()그것이 바로 그 방법이기 때문에.MySQLdb.OperationalError예외.위의 다른 예는 그것이 다음과 같다는 것을 암시합니다.conn.cursor()이 예외를 던지는 메서드입니다.

import MySQLdb

class DB:
  conn = None

  def connect(self):
    self.conn = MySQLdb.connect()

  def query(self, sql):
    try:
      cursor = self.conn.cursor()
      cursor.execute(sql)
    except (AttributeError, MySQLdb.OperationalError):
      self.connect()
      cursor = self.conn.cursor()
      cursor.execute(sql)
    return cursor

db = DB()
sql = "SELECT * FROM foo"
cur = db.query(sql)
# wait a long time for the Mysql connection to timeout
cur = db.query(sql)
# still works

제안된 해결책이 예외를 못 잡아서 문제가 있었습니다.왜 그런지 잘 모르겠습니다.

나는 그 문제를 해결했습니다.ping(True)내가 생각하기에 더 가깝다고 생각하는 진술:

import MySQLdb
con=MySQLdb.Connect()
con.ping(True)
cur=con.cursor()

여기서 확인: http://www.neotitans.com/resources/python/mysql-python-connection-error-2006.html

우분투 Linux를 사용하는 경우 python-mysql 패키지에 동일한 MYSQL_OPT_RECONNECT 옵션을 설정하는 기능이 추가된 패치가 있습니다(여기 참조).저는 안 먹어봤어요.

불행히도 패치는 나중에 자동 연결 및 변환(여기에 설명됨)과의 충돌로 인해 제거되었습니다.

해당 페이지의 의견은 다음과 같습니다. 1.2.2-7 2008-06-19에 용감무쌍하게 공개되었습니다.

python-mysqldb(1.2.2-7) 불안정; 긴급=낮음

[ Sandro Tosi ] * debian/control - 웹페이지에서 재포맷을 피하기 위해 설명에 항목 라인을 2칸으로 시작합니다 (닫힘: #480341)

[ Bernd Zeimetz ] * debian/patchs/02_reconnect.dpatch: - Droping patch: 문제를 설명하는 스톰의 코멘트:

    # Here is another sad story about bad transactional behavior. MySQL
    # offers a feature to automatically reconnect dropped connections.
    # What sounds like a dream, is actually a nightmare for anyone who
    # is dealing with transactions. When a reconnection happens, the
    # currently running transaction is transparently rolled back, and
    # everything that was being done is lost, without notice. Not only
    # that, but the connection may be put back in AUTOCOMMIT mode, even
    # when that's not the default MySQLdb behavior. The MySQL developers
    # quickly understood that this is a terrible idea, and removed the
    # behavior in MySQL 5.0.3. Unfortunately, Debian and Ubuntu still
    # have a patch right now which *reenables* that behavior by default
    # even past version 5.0.3.

개릿과 비슷한 해결책이 필요했지만,cursor.execute(), 내가 허락하고 싶은 대로MySQLdb모든 탈출 임무를 대신 처리해 주시오래퍼 모듈은 결국 다음과 같은 모양이 되었습니다(아래 사용).

#!/usr/bin/env python

import MySQLdb

class DisconnectSafeCursor(object):
    db = None
    cursor = None

    def __init__(self, db, cursor):
        self.db = db
        self.cursor = cursor

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

    def execute(self, *args, **kwargs):
        try:
            return self.cursor.execute(*args, **kwargs)
        except MySQLdb.OperationalError:
            self.db.reconnect()
            self.cursor = self.db.cursor()
            return self.cursor.execute(*args, **kwargs)

    def fetchone(self):
        return self.cursor.fetchone()

    def fetchall(self):
        return self.cursor.fetchall()

class DisconnectSafeConnection(object):
    connect_args = None
    connect_kwargs = None
    conn = None

    def __init__(self, *args, **kwargs):
        self.connect_args = args
        self.connect_kwargs = kwargs
        self.reconnect()

    def reconnect(self):
        self.conn = MySQLdb.connect(*self.connect_args, **self.connect_kwargs)

    def cursor(self, *args, **kwargs):
        cur = self.conn.cursor(*args, **kwargs)
        return DisconnectSafeCursor(self, cur)

    def commit(self):
        self.conn.commit()

    def rollback(self):
        self.conn.rollback()

disconnectSafeConnect = DisconnectSafeConnection

그것을 사용하는 것은 사소한 것이고, 단지 초기 연결만 다를 뿐입니다.MySQLDB의 필요에 따라 래퍼 메서드로 클래스를 확장합니다.

import mydb

db = mydb.disconnectSafeConnect()
# ... use as a regular MySQLdb.connections.Connection object

cursor = db.cursor()

# no more "2006: MySQL server has gone away" exceptions now
cursor.execute("SELECT * FROM foo WHERE bar=%s", ("baz",))

커밋과 클로즈를 분리해서 연결할 수 있습니다...귀엽진 않지만 그래도 돼요.

class SqlManager(object):
 """
 Class that handle the database operation
 """
 def __init__(self,server, database, username, pswd):

      self.server = server
      self.dataBase = database
      self.userID = username
      self.password = pswd

def Close_Transation(self):
      """
      Commit the SQL Query
      """
      try:
        self.conn.commit()
      except Sql.Error, e:
        print "-- reading SQL Error %d: %s" % (e.args[0], e.args[1])

 def Close_db(self):
    try:
        self.conn.close()
    except Sql.Error, e:
        print "-- reading SQL Error %d: %s" % (e.args[0], e.args[1])

 def __del__(self):
    print "close connection with database.."
    self.conn.close() 

MySQL과 Python에도 비슷한 문제가 있었는데, 제게 도움이 된 솔루션은 MySQL을 5.0.27로 업그레이드하는 것이었습니다(페도라 Core 6에서는 시스템이 다른 버전에서 잘 작동할 수 있음).

Python 라이브러리를 패치하는 것을 포함하여 다른 많은 것을 시도했지만 데이터베이스를 업그레이드하는 것이 훨씬 쉬웠고 더 나은 결정이었다고 생각합니다.

Liviu Chircu 솔루션 ...에 추가하여 DisconnectSafeCursor에 다음 메서드를 추가합니다.

def __getattr__(self, name):
    return getattr(self.cursor, name)

"마지막 행"과 같은 원래 커서 속성은 계속 작동합니다.

제 접근 방식은 리비우 치르쿠 솔루션을 기반으로 합니다.

너무 많은 재시도에 대한 통제를 추가했습니다.쿼리가 테이블 구조의 필드와 일치하지 않을 경우 리비우의 원래 답변이 "연결이 너무 많습니다" 작동 오류로 쉽게 이동할 수 있습니다.

MySQLdb.OperationalError: (1054, "Unknown column 'xxxxxxx' in 'field list'")

이 오류는 연결 오류가 아닙니다.제 버전에서는 클래스 변수가 필요 없다고 생각하여 제거했습니다.

import MySQLdb


class MySQLdbReconnectableCursor(object):
    def __init__(self, db, cursor):
        self.db = db
        self.cursor = cursor

    def __getattr__(self, name):
        return getattr(self.cursor, name)

    def __iter__(self, *args, **kwargs):
        return self.cursor.__iter__(*args, **kwargs)

    def __next__(self, *args, **kwargs):
        return self.cursor.__next__(*args, **kwargs)

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

    def execute(self, *args, **kwargs):
        try:
            result = self.cursor.execute(*args, **kwargs)
            self.db.refresh_retries()
            return result
        except MySQLdb.OperationalError:
            self.db.reconnect(reraise=True)
            self.cursor = self.db.cursor()
            return self.cursor.execute(*args, **kwargs)

    def fetchone(self):
        return self.cursor.fetchone()

    def fetchall(self):
        return self.cursor.fetchall()


class MySQLdbReconnectable(object):
    def __init__(self, *args, **kwargs):
        self.conn = None
        self.retries = kwargs.get("max_retries", 3)
        self.connect_args = args
        self.connect_kwargs = kwargs
        self.refresh_retries()
        self.reconnect()

    def refresh_retries(self):
        self.__retries = self.retries

    def reconnect(self, reraise=False):
        if self.__retries:
            self.__retries -= 1
            self.conn = MySQLdb.connect(
                *self.connect_args, **self.connect_kwargs
            )
        else:
            if reraise:
                raise
            else:
                raise IOError("Can not retry anymore!")

    def cursor(self, *args, **kwargs):
        cur = self.conn.cursor(*args, **kwargs)
        return MySQLdbReconnectableCursor(self, cur)

    def commit(self):
        self.conn.commit()

    def rollback(self):
        self.conn.rollback()

다른 사람들은 코드를 사용해서 직접 연결을 끊은 것을 해결하는 것에 베팅합니다.

이를 위한 한 가지 방법은 다음과 같습니다.

import MySQLdb

class DB:
    conn = None

    def connect(self):
        self.conn = MySQLdb.connect()

    def cursor(self):
        try:
            return self.conn.cursor()
        except (AttributeError, MySQLdb.OperationalError):
            self.connect()
            return self.conn.cursor()

db = DB()
cur = db.cursor()
# wait a long time for the Mysql connection to timeout
cur = db.cursor()
# still works

언급URL : https://stackoverflow.com/questions/207981/how-to-enable-mysql-client-auto-re-connect-with-mysqldb

반응형