mysql做為php的黃金搭檔和互聯網上應用最廣泛的數據庫,免不了天天與之打交道,不少朋友在熟悉swoole的使用之后,也趟平了不少坑,准備實戰了,終於上線了,正愉快的體驗swoole帶來的巨大改進,突然數據庫操作bug了,大量報mysql server gone away, 於是swooler心里千萬之草尼馬奔騰而過,大罵,swoole誤我~~~
且慢!!!這真不是swoole的問題!!!!不是swoole的問題!!!!不是swoole的問題!!!!(重要的事情說三遍)
原因
不是swoole的問題,那他的原因是什么呢?
這要從mysql的機制說起,mysql本身是一個多線程的程序,每個連接過來,會開一個線程去處理相關的query, 所以mysql為了避免占着毛坑不拉屎,會定期回收長時間沒有任何query的連接(時間周期受wait_timeout配置影響),所以在swoole中,由於是一個長駐內存的服務,我們建立了一個mysql的連接,不主動關閉 或者是用pconnect的方式,那么這個mysql連接會一直保存着,然后長時間沒有和數據庫有交互,就主動被mysql server關閉了,之后繼續用這個連接,就報mysql server gone away了。
解決方案
知道問題產生的原因,就可以對症下葯了。
方案1: 修改mysql的wait_timeout值為一個非常大的值。
此方法不太可取,可能會產生大量的sleep連接,導致mysql連接上限了, 建議不使用。
方案2:每次query之前主動進行連接檢測
如果是用mysqli,可用內置的mysqli_ping
示例:
if (!$mysqli->ping()) {mysqli->connect(); //重連
}
如果是pdo,可以檢測mysql server的服務器信息來判斷:
try {
$pdo->getAttribute(\PDO::ATTR_SERVER_INFO);
} catch (\Exception $e) {
if ($e->getCode() == 'HY000') {
$pdo = new PDO(xxx); //重連
} else {
throw $e;
}
}
但這個方案有個缺點:額外多一次請求,所以改進方法: 用一個全局變量存放最后一次query的時間,下一次query的時候先和現在時間對比一下,超過waite_timeout再重連. 或者也可以用swoole_tick定時檢測。
方案3:被動檢測, 每次query用try catch包起來,如有mysql gone away異常,則重新連接,再執行一次當前sql.
示例:
try {
query($sql);
} catch (\Exception $e) {
if ($e->getCode() == 'HY000') {
reconnect(); //重連
query($sql)
} else {
throw $e;
}
}
方案4: 用短連接,務必每次操作完之后,手動close