【SQLAlchemy】MySQL server has gone away 原因分析、解决方法
SQLAlchemy報(bào)錯(cuò):MySQL server has gone away
錯(cuò)誤日志
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)
錯(cuò)誤原因
從字面理解,就是你連接的MySQL已經(jīng)走人了,不在了。相當(dāng)于你和另外一個(gè)人打電話,你一直沒有掛電話,但是你把電話放一邊了,直到你重新拎起電話想說點(diǎn)啥,才聽到里面『嘟,嘟,嘟,嘟…』的掛機(jī)聲,于是你就知道電話另一頭的人已經(jīng)gone away了。
線上碰到這個(gè)問題時(shí),通常就是拋一個(gè)異常,然后(主動(dòng)/被動(dòng))重新連接一下,你下次就重新執(zhí)行一下要執(zhí)行的語(yǔ)句就行。這就像你覺得有一番話非說不可,然后重新?lián)芰藗€(gè)電話過去。
什么情況下會(huì)gone away?就像剛才說的,當(dāng)你拿著一個(gè)電話太久,又不說話的時(shí)候,對(duì)面肯定就把你掛了;
當(dāng)sqlalchemy與MySQL建立了一個(gè)連接,而sqlalchemy又不對(duì)這個(gè)連接執(zhí)行任何(有含義)的語(yǔ)句,這個(gè)連接對(duì)于MySQL而言就處于sleep,當(dāng)sleep了太久,MySQL就把連接一頭關(guān)閉了(可能說了句"啥玩意兒")
這時(shí),如果sqlalchemy在這個(gè)連接上嘗試執(zhí)行語(yǔ)句,就會(huì)出現(xiàn)gone away的錯(cuò)誤。
conn.close() 是把連接放回連接池,不是真正的關(guān)閉;池子里的空閑連接在MySQL線程里sleep,長(zhǎng)時(shí)間不操作,MySQL把連接一端關(guān)閉了,所以第二天SQLAlchemy再用這個(gè)連接的時(shí)候,拋出MySQL server has gone away…
解決方法
1. 設(shè)置SQLAlchemy的連接有效期,在MySQL關(guān)閉它之前,我先關(guān)閉它
因?yàn)閟coped_session是threadlocal的,相同線程會(huì)用到相同的session,如果session還持有connection,從pool里checkout connection時(shí)不會(huì)進(jìn)行過期的檢查操作,直接使用,所以必須設(shè)置SQLAIchemy的有效期
SQLAlchemy連接池重新生成的周期默認(rèn)為timeout是2小時(shí),通過 SHOW VARIABLES 可以查看數(shù)據(jù)庫(kù)配置的timeout時(shí)間(如下圖所示),所以設(shè)置sqlalchemy的 "pool_recycle"參數(shù)小于360s,就會(huì)在數(shù)據(jù)庫(kù)服務(wù)器斷開連接之前,自己斷開并重新生成連接
2. 在Web框架的層面,每次請(qǐng)求處理完畢時(shí),顯式地關(guān)閉session。
a. web框架顯示關(guān)閉session的方法有很多,常規(guī)方法如下,在finally中主動(dòng)關(guān)閉seession
b. Django和Flask都有middleware機(jī)制,可以在接收請(qǐng)求之前和處理完請(qǐng)求之后對(duì)session進(jìn)行remove
c. Flask有一類修飾器hook,可以在請(qǐng)求后或請(qǐng)求前做一些事情,使用hook顯示關(guān)閉session如下
了解其他hook修飾器:
before_first_request:注冊(cè)一個(gè)函數(shù),在處理第一個(gè)請(qǐng)求之前運(yùn)行。 before_request:注冊(cè)一個(gè)函數(shù),在每次請(qǐng)求之前運(yùn)行。 after_request:注冊(cè)一個(gè)函數(shù),如果沒有未處理的異常拋出,在每次請(qǐng)求之后運(yùn)行。 teardown_request:注冊(cè)一個(gè)函數(shù),即使有未處理的異常拋出,也在每次請(qǐng)求之后運(yùn)行。 在使用session之前,先檢查其有效性,無效則創(chuàng)建新的session以供使用3. 在使用session之前,先檢查其有效性,無效則創(chuàng)建新的session以供使用
拓展:類似問題 - Lost connection to mysql server during query
(使用的是Flask-SQLAlchemy)
一般由以下四種情況造成, 通過SHOW VARIABLES LIKE ‘’查看一下字段:
1、查詢中大量數(shù)據(jù)被發(fā)送,由于數(shù)據(jù)傳輸時(shí)間不夠導(dǎo)致,可以增加net_read_timeout的值。
net_read_timeout : mysql服務(wù)端從客戶端讀取(接收)數(shù)據(jù)時(shí),服務(wù)端等待客戶端響應(yīng)的超時(shí)時(shí)間,當(dāng)服務(wù)端正在從客戶端讀取數(shù)據(jù)時(shí),net_read_timeout控制何時(shí)超時(shí)
2、初次連接時(shí),連接時(shí)間設(shè)定太少,可以增加connect_timeout的值改善。
connect_timeout:在獲取連接階段(authenticate)起作用, 獲取MySQL連接是多次握手的結(jié)果,除了用戶名和密碼的匹配校驗(yàn)外,還有IP->HOST->DNS->IP驗(yàn)證,任何一步都可能因?yàn)榫W(wǎng)絡(luò)問題導(dǎo)致線程阻塞。為 了防止線程浪費(fèi)在不必要的校驗(yàn)等待上,超過connect_timeout的連接請(qǐng)求將會(huì)被拒絕。
3、有些少見的情況可以show global status like 'aborted_connets',這個(gè)全局變量在每一次服務(wù)器終止時(shí)會(huì)增加1,查看"reading authorization packet"獲取錯(cuò)誤信息。
4、BLOB值太大的問題,調(diào)整配置文件max_allowed_packet。
mysql根據(jù)配置文件會(huì)限制server接受的數(shù)據(jù)包大小。有時(shí)候大的插入和更新會(huì)被max_allowed_packet 參數(shù)限制掉,導(dǎo)致失敗。
拓展:【Python】SQLAlchemy:session何時(shí)commit,何時(shí)close?
Engine 相當(dāng)于一個(gè)創(chuàng)建連接的工廠,而不是連接本身。當(dāng)使用conn.close()時(shí),連接被放回到Engine的連接池當(dāng)中,而不是真正的關(guān)閉了。
如果想要在調(diào)用conn.close()時(shí),真正的關(guān)閉連接,可以使用poolclass=NullPool屬性:
from sqlalchemy.pool import NullPool db = create_engine('mysql://root@localhost/test_database', poolclass=NullPool)拓展:初始問題 - 【Python】SQLAlchemy長(zhǎng)時(shí)間未請(qǐng)求,數(shù)據(jù)庫(kù)連接斷開的原因、解決方案
一個(gè)基于apscheduler的定時(shí)任務(wù),里面的任務(wù)使用了sqlalchemy,這個(gè)任務(wù)每天跑,但是第二天就連不上數(shù)據(jù)庫(kù)
總結(jié)
以上是生活随笔為你收集整理的【SQLAlchemy】MySQL server has gone away 原因分析、解决方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【EasyUI】DataGrid自定义排
- 下一篇: 【NoSQL】NoSQL入门和概述 -