文章目錄
一、前言
本篇主要是通過python
程序連接數據庫的錯誤,從而引出mysql
的wait_timeout
參數和interactive_timeout
的概念,繼而重現程序錯誤,從根源上解決問題。
需求:
python
腳本監聽文件,當文件行數增加10
行則執行一次數據庫持久化操作。只是文件有時候可能需要幾十分鐘才增加10
行,此時操作數據庫會出現:MySQL server has gone away
的錯誤,並且腳本被停止。
根據以上錯誤,首先是要重現MySQL server has gone away
的情況,看看是哪個參數造成的,又該通過哪種方式來捕獲這種錯誤。大家隨便一百度就能知道影響數據庫連接時間的參數一般是wait_timeout
和interactive_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 =10
,interactive_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_timeout
和wait_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