【问题标题】:How to enable MySQL client auto re-connect with MySQLdb?如何启用 MySQL 客户端自动重新连接 MySQLdb?
【发布时间】:2010-09-17 12:05:11
【问题描述】:

我遇到了 PHP 的做法:

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

但 MySQLdb (python-mysql) 没有运气。

谁能给个提示?谢谢。

【问题讨论】:

    标签: python mysql


    【解决方案1】:

    如果您使用的是 ubuntu Linux,python-mysql 软件包中添加了一个补丁,该补丁添加了设置相同 MYSQL_OPT_RECONNECT 选项的功能(请参阅here)。不过我没试过。

    不幸的是,由于与自动连接和事务的冲突(描述为here),该补丁后来被删除。

    该页面上的 cmets 说: 1.2.2-7 intrepid-release于2008-06-19发布

    python-mysqldb (1.2.2-7) 不稳定;紧迫性=低

    [桑德罗·托西] * Debian/控制 - 描述中的列表项目行以 2 个空格开头,以避免重新格式化 在网页上(关闭:#480341)

    [伯恩德·泽梅茨] * debian/patches/02_reconnect.dpatch: - 掉落补丁: Storm 中的评论解释了问题:

        # 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.
    

    【讨论】:

    • 我猜无论客户端做什么,连接丢失都可能表示事务丢失(服务器重启)或连接丢失。所以我同意简单的重新连接可能会隐藏 mysql 客户端用户期望的数据丢失。
    【解决方案2】:

    您也可以自己用代码解决掉线问题。

    一种方法如下:

    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
    

    【讨论】:

    • 其实这个不行。创建游标不会与数据库建立连接,因此不会引发 OperationError 异常。
    【解决方案3】:

    我在使用 MySQL 和 Python 时遇到了类似的问题,对我有用的解决方案是将 MySQL 升级到 5.0.27(在 Fedora Core 6 上;您的系统可能在不同版本下也能正常工作)。

    我尝试了很多其他方法,包括修补 Python 库,但升级数据库要容易得多,而且(我认为)是一个更好的决定。

    【讨论】:

      【解决方案4】:

      我通过创建一个包装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
      

      【讨论】:

      • @garret-heaton - 为什么在 AttributeError 上重新连接?当连接失效时也会抛出这种情况吗?
      • 很长一段时间以来,我试图找出包装器中偶尔出现的 mysql 错误。最初我认为conn.open 可以解决问题……但这有效。
      • 如果您重新连接,您应该知道您丢失了任何@variablesSETtings。此外,一个半完成的交易将是ROLLBACK'd。这可能会导致一些混乱。
      • @aaa90210: AttributeError 是为了捕获 self.conn 仍然为“None”(数据库尚未连接)并且查询方法尝试“cursor = self.conn”的情况。光标()'。
      • 我只想指出,这也会捕获其他错误:OperationalError: (1054, "Unknown column 'countryy' in 'field list'"). 也许这在 2009 年有所不同,但只是说。
      【解决方案5】:

      您可以将连接的提交和关闭分开......这并不可爱,但它确实做到了。

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

      【讨论】:

        【解决方案6】:

        我对建议的解决方案有疑问,因为它没有捕获异常。我不知道为什么。

        我已经用我认为更简洁的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

        【讨论】:

        • 我将我的脚本生成到 16-20 个子进程中,并且我遇到了 mysql 的问题消失了。 con.ping(True) 解决了我的问题。谢谢。
        • 在运行查询之前 Ping 被认为是一种浪费资源且不可靠的反模式:percona.com/blog/2010/05/05/…
        【解决方案7】:

        我需要一个与 Garret 类似的解决方案,但对于 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",))
        

        【讨论】:

        • 这应该是第一个答案。比其他人更强大。
        • 真的很好!谢谢!
        【解决方案8】:

        除了 Liviu Chircu 解决方案...在 DisconnectSafeCursor 中添加以下方法:

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

        并且像“lastrowid”这样的原始光标属性将继续工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-02-27
          • 1970-01-01
          • 2018-09-17
          • 2017-08-27
          • 2012-07-24
          • 2014-02-17
          • 1970-01-01
          相关资源
          最近更新 更多