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方法中添加第二個參數並從那里開始。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM