MySQL server has gone away(錯誤碼 2006)代表客戶端與伺服器的連線中斷。兩大主因:
wait_timeout(預設 8 小時)無活動,伺服器主動斷開max_allowed_packet(預設 4/64 MB),伺服器拒絕並斷開my.cnf / my.ini[mysqld]
# 閒置逾時延長為 30 天(單位:秒),視業務調整
wait_timeout = 2592000
interactive_timeout = 2592000
# 最大封包調大至 64MB(視 BLOB/TEXT 大小調整)
max_allowed_packet = 64M
重啟 MySQL 生效。缺點:佔用更多連線資源、記憶體;無法解決網路抖動、伺服器重啟導致的斷線。
$mysqli = new mysqli("localhost", "user", "pass", "db");
$mysqli->query("SET SESSION wait_timeout = 2592000");
$mysqli->query("SET SESSION max_allowed_packet = 67108864"); // 64MB
或使用 MYSQLI_CLIENT_INTERACTIVE 旗標啟用 interactive_timeout:
$mysqli = mysqli_connect("localhost", "user", "pass", "db", 3306, NULL, MYSQLI_CLIENT_INTERACTIVE);
mysql_ping() 單獨使用不可靠?mysql_ping() 不會自動重連,僅回傳連線狀態MYSQL_OPT_RECONNECT 選項(C API),但 PHP mysqli / PDO 未直接暴露class Db {
private $mysqli;
private $host, $user, $pass, $db, $port;
public function __construct($host, $user, $pass, $db, $port = 3306) {
$this->host = $host; $this->user = $user; $this->pass = $pass; $this->db = $db; $this->port = $port;
$this->connect();
}
private function connect() {
$this->mysqli = new mysqli($this->host, $this->user, $this->pass, $this->db, $this->port);
if ($this->mysqli->connect_errno) {
throw new Exception("Connect failed: " . $this->mysqli->connect_error);
}
$this->mysqli->set_charset("utf8mb4");
}
/** 檢查連線,斷線則重連 */
public function ping() {
if (!$this->mysqli->ping()) { // 回傳 false 表示斷線
$this->mysqli->close(); // 【關鍵】必須先關閉舊連線物件
$this->connect(); // 重新建立
}
}
public function query($sql) {
$this->ping(); // 每次查詢前自動檢查
return $this->mysqli->query($sql);
}
// 其他 prepare、execute、fetch 等方法同理先呼叫 $this->ping()
}
class PdoDb {
private $pdo;
private $dsn, $user, $pass, $options;
public function __construct($dsn, $user, $pass, $options = []) {
$this->dsn = $dsn; $this->user = $user; $this->pass = $pass; $this->options = $options;
$this->connect();
}
private function connect() {
$this->pdo = new PDO($this->dsn, $this->user, $this->pass, $this->options);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo->exec("SET NAMES utf8mb4");
}
public function ping() {
try {
// PDO 無 ping(),執行輕量查詢測試
$this->pdo->query("SELECT 1");
} catch (PDOException $e) {
$this->pdo = null; // 釋放舊連線
$this->connect(); // 重連
}
}
public function query($sql) {
$this->ping();
return $this->pdo->query($sql);
}
}
import pymysql
from contextlib import contextmanager
class PyDb:
def __init__(self, **cfg):
self.cfg = cfg
self.conn = None
self.connect()
def connect(self):
self.conn = pymysql.connect(**self.cfg, charset='utf8mb4', autocommit=True)
def ping(self):
try:
self.conn.ping(reconnect=True) # PyMySQL 內建 reconnect 參數
except:
self.connect()
@contextmanager
def cursor(self):
self.ping()
cursor = self.conn.cursor()
try:
yield cursor
finally:
cursor.close()
針對「長時間執行批次 SQL(如爬蟲、ETL)」導致中間閒置超時:
ping()COMMIT / 執行 SELECT 1 保持連線活躍HikariCP、DBUtils、SQLAlchemy pool)自動管理連線生命週期| 方案 | 優點 | 缺點 | 推薦度 |
|---|---|---|---|
| 調大 wait_timeout / max_allowed_packet | 簡單、無需改代碼 | 資源浪費、無法應對網路/重啟斷線 | ⭐⭐ |
| 應用層 ping + 重連封裝 | 徹底解決所有斷線場景、可控 | 需修改代碼、引入微小延遲 | ⭐⭐⭐⭐⭐ |
| 使用成熟連線池庫 | 企業級、自動驗證/重連/回收 | 學習成本、依賴第三方 | ⭐⭐⭐⭐ |
最佳實務:方案二 + 連線池。在資料庫存取層(DAO/Repository)統一實作 ping() 機制,上層業務邏輯完全無感,系統穩定性大幅提升。