zoukankan      html  css  js  c++  java
  • pymysql检查是否断开, 断开重连

    python mysql使用持久链接

    python链接mysql中没有长链接的概念,但我们可以利用mysql的ping机制,来实现长链接功能~

    思路:

    1 python mysql 的cping 函数会校验链接的可用性,如果连接不可用将会产生异常

    2 利用这一特性,构造一个连接丢失的循环,不断尝试连接数据库,直到连接恢复

    3 使用这样的机制不需要关闭数据库功能,对于驻留进程,有大量数据进行写操作时,很有用途

    #!/usr/bin/env python
    # -*-coding:UTF-8-*-
    import sys, MySQLdb, traceback
    import time
    
    
    class mysql:
        def __init__(self,
                     host='',
                     user='',
                     passwd='',
                     db='',
                     port=3306,
                     charset='utf8'
                     ):
            self.host = host
            self.user = user
            self.passwd = passwd
            self.db = db
            self.port = port
            self.charset = charset
            self.conn = None
            self._conn()
    
        def _conn(self):
            try:
                self.conn = MySQLdb.Connection(self.host, self.user, self.passwd, self.db, self.port, self.charset)
                return True
            except:
                return False
    
        def _reConn(self, num=28800, stime=3):  # 重试连接总次数为1天,这里根据实际情况自己设置,如果服务器宕机1天都没发现就......
            _number = 0
            _status = True
            while _status and _number <= num:
                try:
                    self.conn.ping()  # cping 校验连接是否异常
                    _status = False
                except:
                    if self._conn() == True:  # 重新连接,成功退出
                        _status = False
                        break
                    _number += 1
                    time.sleep(stime)  # 连接不成功,休眠3秒钟,继续循环,知道成功或重试次数结束
    
        def select(self, sql=''):
            try:
                self._reConn()
                self.cursor = self.conn.cursor(MySQLdb.cursors.DictCursor)
                self.cursor.execute(sql)
                result = self.cursor.fetchall()
                self.cursor.close()
                return result
            except MySQLdb.Error, e:
                # print "Error %d: %s" % (e.args[0], e.args[1])
                return False
    
        def select_limit(self, sql='', offset=0, length=20):
            sql = '%s limit %d , %d ;' % (sql, offset, length)
            return self.select(sql)
    
        def query(self, sql=''):
            try:
                self._reConn()
                self.cursor = self.conn.cursor(MySQLdb.cursors.DictCursor)
                self.cursor.execute("set names utf8")  # utf8 字符集
                result = self.cursor.execute(sql)
                self.conn.commit()
                self.cursor.close()
                return (True, result)
            except MySQLdb.Error, e:
                return False
    
        def close(self):
            self.conn.close()
    
    
    if __name__ == '__main__':
        my = mysql('localhost', 'root', 'root', 'test', 3306)
        print my.select_limit('select * from a_table', 1, 1)
        # my.close()

    test

    【Python】数据库异常pymysql.err.InterfaceError: (0, '')解决方案

     

    后台服务在运行时发现一个问题,运行一段时间后,接口请求报错;

    pymysql.err.InterfaceError: (0, '')

    排查到原因是数据库操作对象实例未注销,但是持有的数据库连接已经过期,导致后续数据库操作不能正常进行;

    出现问题的代码

    class MysqlConnection(object):
     
        """
        mysql操作类,对mysql数据库进行增删改查
        """
     
        def __init__(self, config):
            # Connect to the database
            self.connection = pymysql.connect(**config)
            self.cursor = self.connection.cursor()
     
        def Query(self, sql):
            """
            查询数据
            :param sql:
            :return:
            """
            self.cursor.execute(sql)
            return self.cursor.fetchall()
    

    在分析问题前,先看看Python 数据库的Connection、Cursor两大对象

    Python 数据库

    Connection()的参数列表
    host,连接的数据库服务器主机名,默认为本地主机(localhost)
    user,连接数据库的用户名,默认为当前用户
    passwd,连接密码,没有默认值
    db,连接的数据库名,没有默认值
    conv,将文字映射到Python类型的字典
    cursorclass,cursor()使用的种类,默认值为MySQLdb.cursors.Cursor
    compress,启用协议压缩功能
    named_pipe,在windows中,与一个命名管道相连接
    init_command,一旦连接建立,就为数据库服务器指定一条语句来运行
    read_default_file,使用指定的MySQL配置文件
    read_default_group,读取的默认组
    unix_socket,在unix中,连接使用的套接字,默认使用TCP
    port,指定数据库服务器的连接端口,默认是3306
    

    排查:可能是因为对象属性cursor引起的

    网上有提到cursor引起的,其实不一定,为了排除这个假设,我在每个操作数据库的方法里都重新引用connect.curser()

    cursor = connection.cursor()
    cursor.execute(query)
    cursor.close()

    去掉__init__方法里的self.cursor,运行一段时间后,还是出现异常,所以可以断定跟cursor没有关系;
    ##排查:查看数据库对象
    调试代码,将超时时间设置较长

    self.connection._write_timeout = 10000

    发现并没有生效,使用try…except… 方法捕获失败后重新连接数据库

    try:
        self.cursor.execute(sql)
    except:
        self.connection()
        self.cursor.execute(sql)

    直接抛出异常,并没有执行except代码段
    打印self.connection ,输出如下:

    <pymysql.connections.Connection object at 0x0000000003E2CCC0>

    抛出异常重新connect是不行的,因为connections 仍存在未失效,也就是我们找到的原因:对象持有的数据库连接断开了

    解决

    找到一种方法可以解决问题,在每次连接之前,判断该链接是否有效,pymysql提供的接口是 Connection.ping()

    	#这个该方法的源码
        def ping(self, reconnect=True):
            """Check if the server is alive"""
            if self._sock is None:
                if reconnect:
                    self.connect()
                    reconnect = False
                else:
                    raise err.Error("Already closed")
            try:
                self._execute_command(COMMAND.COM_PING, "")
                return self._read_ok_packet()
            except Exception:
                if reconnect:
                    self.connect()
                    return self.ping(False)
                else:
                    raise
    

    所以每次处理数据库的时候,可以这么操作,现实发现方案有效;

    class DataSource(object):
    
        def __init__(self):
            self.conn = self.to_connect()
    
        def __del__(self):
            self.conn.close()
    
        def to_connect(self):
            return pymysql.connections.Connection(**params)
    
        def is_connected(self):
            """Check if the server is alive"""
            try:
                self.conn.ping(reconnect=True)
                print "db is connecting"
            except:
                traceback.print_exc()
                self.conn = self.to_connect()
                print "db reconnect"

    补充

    数据库之所以断开连接,是因为数据库默认的wait_timeout=28800,这个单位是秒,换算后是8小时,也就是原来我的服务启动8小时后,就会被mysql自动断开,如果我没有重连机制那就真的是不能用,这个不像java的一些orm框架都能做连接存活处理,其实python对应的orm框架sqlalchemy也有这个处理,但是我选择了pymysql,所以需要自己处理。

    --查询数据库的各项超时指标
    show variables like  '%timeout%';

    pymysql检查是否断开, 断开重连

      简单的流程是这样子

    1. db.ping(reconnect=True)
    2. cur.execute(sql)
    3.  db.commit()

     

    Python mysql(使用pymysql)自动重新连接

    最简单的方法是在发送查询之前检查连接。

    您可以通过创建包含两个方法的小类来完成此操作:connectquery

    import pymysql
    import pymysql.cursors
    
    class DB:
        def connect(self):
            self.conn = pymysql.connect(
                                 host=hostname,
                                 user=username,
                                 password=password,
                                 db=dbname,
                                 charset='utf8mb4',
                                 cursorclass=pymysql.cursors.DictCursor,
                                 port=3306)
    
        def query(self, sql):
            try:
                cursor = self.conn.cursor()
                cursor.execute(sql)
            except pymysql.OperationalError:
                self.connect()
                cursor = self.conn.cursor()
                cursor.execute(sql)
            return cursor
    
    db = DB()
    

    现在,无论何时使用db.query("example SQL")请求发送查询,都会自动准备遇到连接错误,并self.connect()在需要时重新连接。

    记住:这是一个简化的例子。通常,您希望让PyMySQL帮助您在查询中转义特殊字符。要做到这一点,你必须在query方法中添加第二个参数并从那里开始。

     
  • 相关阅读:
    hdu 3342 Legal or Not 拓排序
    hdu 1596 find the safest road Dijkstra
    hdu 1874 畅通工程续 Dijkstra
    poj 2676 sudoku dfs
    poj 2251 BFS
    poj Prime Path BFS
    poj 3278 BFS
    poj 2387 Dijkstra 模板
    poj 3083 DFS 和BFS
    poj 1062 昂贵的聘礼 dijkstra
  • 原文地址:https://www.cnblogs.com/leijiangtao/p/11882107.html
Copyright © 2011-2022 走看看