python重現 mysql server has gone away錯誤以及解決方案

一、前言

       本篇主要是通過python程序連接數據庫的錯誤,從而引出mysqlwait_timeout 參數和interactive_timeout的概念,繼而重現程序錯誤,從根源上解決問題。

需求:
       python腳本監聽文件,當文件行數增加10行則執行一次數據庫持久化操作。只是文件有時候可能需要幾十分鐘才增加10行,此時操作數據庫會出現:MySQL server has gone away 的錯誤,並且腳本被停止。

       根據以上錯誤,首先是要重現MySQL server has gone away的情況,看看是哪個參數造成的,又該通過哪種方式來捕獲這種錯誤。大家隨便一百度就能知道影響數據庫連接時間的參數一般是wait_timeoutinteractive_timeout 兩個參數,下面就着重重現一下錯誤,並總結下遇到的問題

關於python如何捕獲mysql的各種錯誤,可以參考我的上篇文章:python怎麼捕獲mysql報錯

二、瞭解wait_timeout 和interactive_timeout 兩個參數

       爲了重現錯誤,我們設置兩個參數都爲10s,即10s後數據庫就自動斷開連接了,先通過命令行測試下看看。

1、命令行操作

不管是設置wait_timeout 還是設置 interactive_timeout = 10,執行sql命令行都會報錯:

錯誤1 :

ERROR 2013 (HY000): Lost connection to MySQL server during query

重新執行sql,則會重新連接,此時的錯誤纔是我們想要的 gone away

錯誤2 :

mysql> show variables like '%timeout%';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    27
Current database: *** NONE ***

       本來想着會直接報:MySQL server has gone away 的錯誤,沒想到先報的是數據庫失去連接錯誤,博主懷疑這兩個參數是否會影響兩種錯誤的產生。

2、wait_time 設置失效問題

       這個剛開始還挺奇怪的,明明設置的wait_time =10interactive_timeout =1000,但是實際查看的時候,總是發現wait_time =1000 了,真是奇了怪了,爲何這個參數會自己改變呢?

修改wait_timeout不生效的問題:

https://www.cnblogs.com/azhqiang/p/5454000.html

       通過這個鏈接可以發現,實際上wait_timeout是受interactive_timeout 影響,只是爲什麼會出現這種情況呢?

3、參考手冊概念,解釋兩個參數

interactive_timeout:

The number of seconds the server waits for activity on an interactive connection before closing it. An interactive client is defined as a client that uses the CLIENT_INTERACTIVE option to mysql_real_connect()

wait_timeout:

The number of seconds the server waits for activity on a noninteractive connection before closing it. On thread startup, the session wait_timeout value is initialized from the global wait_timeout value or from the global interactive_timeout value,
 depending on the type of client (as defined by the CLIENT_INTERACTIVE connect option to mysql_real_connect())

       他們都是session/global級別的,簡單的說前者用於描述交互式的客戶端的空閒超時,後者用於非交互式的客戶端的空閒超時,但是這裏也揭示了,如果是交互式客戶端連接的session那麼wait_timeout將被interactive_timeout覆蓋掉,換句話說如果是非交互式的客戶端連接的session將不會使用interactive_timeout覆蓋掉wait_timeout,也就是interactive_timeout沒有任何作用了。一旦會話登陸成功,如果想要會話級別修改超時參數,不管交互式還是非交互式都是修改wait_timeout參數纔會生效。

參考:

http://blog.itpub.net/7728585/viewspace-2637237/

這篇文章解析的相當清楚,例子也很生動,總結來說就是:

       如果是交互式客戶端連接的session那麼wait_timeout將被interactive_timeout覆蓋掉,換句話說如果是非交互式的客戶端連接的session將不會使用interactive_timeout覆蓋掉wait_timeout,也就是interactive_timeout沒有任何作用了。

4、那麼什麼算是交互式,什麼算是非交互式呢

交互式操作:
       通俗的說,就是你在你的本機上打開mysql的客戶端,就是那個黑窗口,在黑窗口下進行各種sql操作,當然走的肯定是tcp協議。(小黑框方式)

非交互式操作:
       就是你在你的項目中進行程序調用。比如一邊是tomcat web服務器,一邊是數據庫服務器,兩者怎麼通信?在java web裏,我們通常會選擇hibernate或者是jdbc來連接。那麼這時候就是非交互式操作。 (代碼連接方式)

       根據上面所述的,之前命令行測試屬於交互式操作,那咱們程序連接的應該就是非交互式連接,如果想要程序重現這個問題,那麼需要設置非交互式的wait_time的值

三、python重現 mysql server has gone away

       到底哪個參數會造成錯誤1,哪些參數會造成 錯誤2呢。在命令行測試的時候,發現總是先出現錯誤1,再出現錯誤2,那麼這兩個錯誤是否是分開受到兩個參數的影響呢?如何重現出來我們想要的: mysql server has gone away 錯誤。

通過代碼測試,不繼續在命令行測試了。代碼爲非交互式操作。

1、通過代碼測試兩個參數影響

(1)當兩個參數都設置爲10s的時候:

 try:
           conn.ping()
        except MySQLdb.Error, e:
            print 'error'
            # sqlError = "Error %d:%s" % (e.args[0], e.args[1])
            print e.args[0]
            print e.args[1]
            try:
                conn.ping()
            except MySQLdb.OperationalError,e:
                print 'error1'
                print e
            pass

這段代碼打印如下:

error
2013
Lost connection to MySQL server during query
error1
(2006, 'MySQL server has gone away')

       經過試驗,還是沒直接獲得MySQL server has gone away的錯誤,都是先報數據庫失去連接,然後再進行重連,此時才報出:MySQL server has gone away

(2)當wait_timeout =10,而interactive_timeout =1000

error
2013
Lost connection to MySQL server during query
2013
error1
(2006, 'MySQL server has gone away')
2222_2

結果和上面一致,並沒有單獨報MySQL server has gone away的錯誤。

(3)當interactive_timeout =10,而wait_timeout =1000

       沒有報錯,程序正常執行。當代碼連接mysql的時候,相當於是非交互式,那麼此時的interactive_timeout 相當於沒用了,用的是wait_timeout 的值,所以程序沒報錯。

(4)設置兩個參數都爲10s,程序sleep(15)看看效果

import time

time.sleep(15)

error
2013
Lost connection to MySQL server during query
2013
error1
(2006, 'MySQL server has gone away')

       通過頻繁的測試發現,出現錯誤1錯誤2似乎並不是這兩個參數來操縱的,因爲這兩個錯誤總是一起出現的,這就和原來的猜想不一樣了。不過仔細想想也是,你想要操作Mysql,肯定是要先檢查是否存在子線程的,發現不存在就報個錯,重新連接失敗再報個錯,比較符合邏輯

2、不設置重連,直接執行相應的sql看看

try:
            cursor.execute(insert_sql)  # 直接執行sql
        except MySQLdb.Error, e:
            print 'error'
            # sqlError = "Error %d:%s" % (e.args[0], e.args[1])
            print e.args[0]
            print e.args[1]
            print e[0]
            try:
                cursor.execute(insert_sql)
            except MySQLdb.OperationalError, e:
                print 'error1'
                print e
            pass

打印結果如下:

error
2006
MySQL server has gone away
2006
error1
(2006, 'MySQL server has gone away')

       這裏發現,在直接執行sql的時候,會直接報MySQL server has gone away的錯誤。OK,報錯已經重現。

3、總結

       這個報錯並不是interactive_timeoutwait_timeout 這兩個參數影響的。估計是,mysql在直接執行sql的時候,會主動去線程池尋找子進程,找不到的時候,先拋出Lost connection to MySQL server during query錯誤,然後嘗試重連的時候拋出MySQL server has gone away錯誤。

       我們上面的代碼是一直在嘗試重連,所以先拋出錯誤1,再拋出錯誤2 。而直接執行sql的時候,錯誤1錯誤2給覆蓋了,因此打印出來的MySQL server has gone away,實際上錯誤1 依然存在。

四、捕獲mysql錯誤並重新連接的完整代碼

代碼如下:

 try:
            cursor.execute(insert_sql)
        except MySQLdb.OperationalError:  # 先檢測數據庫連接的問題,檢測無誤則執行sql
            print 'connect error1'
            cursor = MakeDbConnection()	  # 重新連接sql,函數裏面的是connect()方法
try:
          cursor.execute(insert_sql)
 except MySQLdb.ProgrammingError:    # 捕獲sql語句執行的錯誤
        try:
              xxxxxxxxxxxxxxx 		 # 根據各自的需求,重新調整下
             cursor.execute(insert_sql)     	#重新執行sql
        except:
             pass
 except UnicodeDecodeError:     # 捕獲編碼錯誤,如捕獲代碼不是utf-8的錯誤
          pass

       如此便可避免數據庫丟失連接的錯誤,捕獲到連接失敗,則重新連接,然後執行代碼即可,媽媽再也不用擔心我的腳本會自己停掉了。

end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章