PHP中MySQL server has gone away問題
一.背景
之前在Codeigniter里面寫過類似console命令行的腳本. 腳本里存在sleep語句時間比較久, 導致出現一個現象就是sleep之前的SQL都是操作成功的,但是sleep之后,再執行SQL操作竟然報錯: MySQL server has gone away. 也就是mysql的這個連接失效. 后來分析才知道, MySQL中存在2個重要的配置參數:
interactive_timeout
wait_timeout
這2個參數的單位都是秒(s). 默認是8小時(28800). interactive_timeout從單詞上看指的是交互超時時間. mysql的連接方式一般分為2種, 一種稱之為"交互式", 一種稱為"非交互式". 一般常見的使用mysql -u root xxx之類的或者主從復制的連接為"交互式連接", 使用如Java的JDBC、PHP的PDO驅動連接的方式一般是"非交互式連接". 然而interactive_timeout如果未修改的情況下,這個值是一直不會變的,但是wait_timeout在不同連接方式下,值是不一樣的.
wait_timeout在"交互式連接"下, 其值是interactive_timeout的值. 如果在"非交互式連接"情況下, 則wait_timeout的值是原來mysql.cnf中配置的原始值.
最終起作用的只是wait_timeout的值.這配置項限定了處于sleep狀態(通過 show processlist查看當前連接數情況)的連接,如果這個連接sleep休眠時間超過wait_timeout的值,則這個連接被斷掉或者說被清理掉.
二.wait_timeout分析
1.首先查看mysql.conf配置

首先我們配置了interactive_timeout=10 wait_timeout=5, 此時通過mysql客戶端(交互式連接)查看這2個配置項的值: show variables like ‘%timeout%';
2.交互式連接

客戶端的結果: wait_timeout竟然不是我們msyql.conf配置的10s, 而是 5s.
那我們再來看看PHP連接MySQL(非交互式連接),執行相同的語句,得到什么結構:
3.非交互式連接


此時wait_timeout是我們原來在mysql.cnf配置的值了.
綜上所述: wait_timeout這個值,在不同的"連接模式"下面,拿到的值是不一樣的.
三.gone away原因分析
結合上面的情況,我們就知道了。 一開始某些SQL執行成功,但是后面的SQL執行失敗報錯gone away,大部分原因就是這個連接被閑置超過了wait_timeout,mysql服務器單方面斷掉了這個連接。但是客戶端代碼,還是在用這個連接變量,以為連接還是ok的(其實mysql server端已經斷開了,只是我們以為這個連接還有效),去執行SQL必然報錯.
那么我們怎么解決這個情況呢?
1.可以適當調整wait_timeout的值, 調大一點,這樣不容易觸發這個gone away情況.但是弊端就是,sleep的長連接不被清理,資源白白浪費了.
2.通過try-cach如果拋出gone way msyql的連接問題, 先把之前的db調用close().在重新獲取db連接open,然后再執行之前的代碼. 不過代碼看起來感覺很蛋疼.偽代碼:
$db = db();
try {
fun1$db); // 一開始執行成功
sleep(3600*10) // 假設sleep了10個小時
fun2($db); // 10小時之后 由于連接已經被mysql干掉 導致報錯 gone away
}catch(Exception $e) {
// 報錯后 我們把無效的連接close 在 open新的連接
$db->close();
$db->open();
// 再拿到新的連接執行
// fun2($db)
}
3.如果是使用類似swoole或者easyswoole框架, 建議使用mysql pool連接池的形式.并且一般連接池都有關于心跳檢查ping、連接存活檢測間隔時間設置、最大閑置連接數等等設置, 只要配置一次就好了。 例如可以配置測活連接間隔時間短一點,來保證連接不會被msyql服務器干掉.例如 easyswoole配置:

例如之前我設置wait_timeout=10, 但是如果我沒修改這個easyswoole的mysql連接池測活間隔時間變小, 同樣會出現gone way的情況. 第一次訪問接口成功返回SQL執行結果,但是超過10s以后我再次訪問接口,報錯mysql has gone away。修改setIntervalCheckTime()之后,就不會出現這個問題了. 我們通過mysql的show processlist;查看連接數情況:

這些都是easyswoole幫我們維護的連接數. 當sleep超過3秒時, 由于檢查時間是3秒存活, 連接池幫我們保活檢查, sleep的時間又從0開始計算.
下面是其他網友的補充
進入MySQL
cmd
mysql -u用戶名 -p密碼
在我們使用mysql導入大文件sql時可能會報MySQL server has gone away錯誤,該問題是max_allowed_packet配置的默認值設置太小,只需要相應調大該項的值之后再次導入便能成功。該項的作用是限制mysql服務端接收到的包的大小,因此如果導入的文件過大則可能會超過該項設置的值從而導致導入不成功!下面我們來看一下如何查看以及設置該項的值。
查看 max_allowed_packet 的值
show global variables like 'max_allowed_packet';
+--------------------+---------+
| Variable_name | Value |
+--------------------+---------+
| max_allowed_packet | 4194304 |
+--------------------+---------+
可以看到默認情況下該項的大小只有4M,接下來將該值設置成150M(1024*1024*150)
set global max_allowed_packet=157286400;
此時再查看大小
show global variables like 'max_allowed_packet';

通過調大該值,一般來說再次導入數據量大的sql應該就能成功了,如果任然報錯,則繼續再調大一些就行,請注意通過在命令行中進行設置只對當前有效,重啟mysql服務之后則恢復默認值,但可以通過修改配置文件(可以在配置文件my.cnf中添加max_allowed_packet=150M即可)來達到永久有效的目的,可其實我們并不是經常有這種大量數據的導入操作,所以個人覺得通過命令行使得當前配置生效即可,沒有必要修改配置文件。
以上就是本次介紹的全部相關知識點內容,希望腳本之家整理的內容能夠幫助到你。
您可能感興趣的文章:- Mysql主從同步Last_IO_Errno:1236錯誤解決方法
- MySQL Daemon failed to start錯誤解決辦法
- mysql “ Every derived table must have its own alias”出現錯誤解決辦法
- MySQL啟動錯誤解決方法