萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

解決 MySQL "server has gone away" 兩大核心方案:參數調優與連線自動重連機制

錯誤成因分析

MySQL server has gone away(錯誤碼 2006)代表客戶端與伺服器的連線中斷。兩大主因:

  1. 閒置逾時:連線超過 wait_timeout(預設 8 小時)無活動,伺服器主動斷開
  2. 封包過大:單次 SQL/結果超過 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 生效。缺點:佔用更多連線資源、記憶體;無法解決網路抖動、伺服器重啟導致的斷線。

PHP 運行期動態設定(無權改 my.cnf 時)

$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 未直接暴露

正確模式:偵測斷線 → 關閉舊連線 → 建立新連線

PHP (mysqli) 封裝範例

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()
}

PHP (PDO) 封裝範例

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);
    }
}

Python (PyMySQL) 範例

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() 機制,上層業務邏輯完全無感,系統穩定性大幅提升。

copyright © 萬盛學電腦網 all rights reserved