【SQLAlchemy】MySQL server has gone away 原因分析、解决方法
SQLAlchemy報錯:MySQL server has gone away
錯誤日志
2020-01-03 20:00:00,072 - update_example_table.py - get_pcodes_arr_by_kind_from_db[line:147] - ERROR: (pymysql.err.OperationalError) (2006, “MySQL server has gone away (error(10053, ‘’))”)
[SQL: SELECT example_menu.p_codes AS example_menu_p_codes
FROM example_menu
WHERE example_menu.isactive = %(isactive_1)s AND example_menu.kind = %(kind_1)s]
[parameters: {u’kind_1’: ‘BLACK’, u’isactive_1’: 1}]
(Background on this error at: http://sqlalche.me/e/e3q8)
錯誤原因
從字面理解,就是你連接的MySQL已經走人了,不在了。相當于你和另外一個人打電話,你一直沒有掛電話,但是你把電話放一邊了,直到你重新拎起電話想說點啥,才聽到里面『嘟,嘟,嘟,嘟…』的掛機聲,于是你就知道電話另一頭的人已經gone away了。
線上碰到這個問題時,通常就是拋一個異常,然后(主動/被動)重新連接一下,你下次就重新執行一下要執行的語句就行。這就像你覺得有一番話非說不可,然后重新撥了個電話過去。
什么情況下會gone away?就像剛才說的,當你拿著一個電話太久,又不說話的時候,對面肯定就把你掛了;
當sqlalchemy與MySQL建立了一個連接,而sqlalchemy又不對這個連接執行任何(有含義)的語句,這個連接對于MySQL而言就處于sleep,當sleep了太久,MySQL就把連接一頭關閉了(可能說了句"啥玩意兒")
這時,如果sqlalchemy在這個連接上嘗試執行語句,就會出現gone away的錯誤。
conn.close() 是把連接放回連接池,不是真正的關閉;池子里的空閑連接在MySQL線程里sleep,長時間不操作,MySQL把連接一端關閉了,所以第二天SQLAlchemy再用這個連接的時候,拋出MySQL server has gone away…
解決方法
1. 設置SQLAlchemy的連接有效期,在MySQL關閉它之前,我先關閉它
因為scoped_session是threadlocal的,相同線程會用到相同的session,如果session還持有connection,從pool里checkout connection時不會進行過期的檢查操作,直接使用,所以必須設置SQLAIchemy的有效期
SQLAlchemy連接池重新生成的周期默認為timeout是2小時,通過 SHOW VARIABLES 可以查看數據庫配置的timeout時間(如下圖所示),所以設置sqlalchemy的 "pool_recycle"參數小于360s,就會在數據庫服務器斷開連接之前,自己斷開并重新生成連接
2. 在Web框架的層面,每次請求處理完畢時,顯式地關閉session。
a. web框架顯示關閉session的方法有很多,常規方法如下,在finally中主動關閉seession
b. Django和Flask都有middleware機制,可以在接收請求之前和處理完請求之后對session進行remove
c. Flask有一類修飾器hook,可以在請求后或請求前做一些事情,使用hook顯示關閉session如下
了解其他hook修飾器:
before_first_request:注冊一個函數,在處理第一個請求之前運行。 before_request:注冊一個函數,在每次請求之前運行。 after_request:注冊一個函數,如果沒有未處理的異常拋出,在每次請求之后運行。 teardown_request:注冊一個函數,即使有未處理的異常拋出,也在每次請求之后運行。 在使用session之前,先檢查其有效性,無效則創建新的session以供使用3. 在使用session之前,先檢查其有效性,無效則創建新的session以供使用
拓展:類似問題 - Lost connection to mysql server during query
(使用的是Flask-SQLAlchemy)
一般由以下四種情況造成, 通過SHOW VARIABLES LIKE ‘’查看一下字段:
1、查詢中大量數據被發送,由于數據傳輸時間不夠導致,可以增加net_read_timeout的值。
net_read_timeout : mysql服務端從客戶端讀取(接收)數據時,服務端等待客戶端響應的超時時間,當服務端正在從客戶端讀取數據時,net_read_timeout控制何時超時
2、初次連接時,連接時間設定太少,可以增加connect_timeout的值改善。
connect_timeout:在獲取連接階段(authenticate)起作用, 獲取MySQL連接是多次握手的結果,除了用戶名和密碼的匹配校驗外,還有IP->HOST->DNS->IP驗證,任何一步都可能因為網絡問題導致線程阻塞。為 了防止線程浪費在不必要的校驗等待上,超過connect_timeout的連接請求將會被拒絕。
3、有些少見的情況可以show global status like 'aborted_connets',這個全局變量在每一次服務器終止時會增加1,查看"reading authorization packet"獲取錯誤信息。
4、BLOB值太大的問題,調整配置文件max_allowed_packet。
mysql根據配置文件會限制server接受的數據包大小。有時候大的插入和更新會被max_allowed_packet 參數限制掉,導致失敗。
拓展:【Python】SQLAlchemy:session何時commit,何時close?
Engine 相當于一個創建連接的工廠,而不是連接本身。當使用conn.close()時,連接被放回到Engine的連接池當中,而不是真正的關閉了。
如果想要在調用conn.close()時,真正的關閉連接,可以使用poolclass=NullPool屬性:
from sqlalchemy.pool import NullPool db = create_engine('mysql://root@localhost/test_database', poolclass=NullPool)拓展:初始問題 - 【Python】SQLAlchemy長時間未請求,數據庫連接斷開的原因、解決方案
一個基于apscheduler的定時任務,里面的任務使用了sqlalchemy,這個任務每天跑,但是第二天就連不上數據庫
總結
以上是生活随笔為你收集整理的【SQLAlchemy】MySQL server has gone away 原因分析、解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【EasyUI】DataGrid自定义排
- 下一篇: 【MS Word技巧】word如何批量把