首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL C API 斷線重連機制深度解析:mysql_ping、MYSQL_OPT_RECONNECT 與自動重連陷阱

     編輯:MySQL 綜合教學

MySQL C API 自動重連全鏈路源碼級分析

問題背景

長連接超過 wait_timeout (默認8小時) 後,Server 主動斷開,客戶端再次查詢報錯:2006: MySQL server has gone away

關鍵配置:MYSQL_OPT_RECONNECT

MYSQL *mysql = mysql_init(NULL);
mysql_real_connect(mysql, "host", "user", "pass", "db", 3306, NULL, 0);

// 必須在 mysql_real_connect 之後設置 (5.0.19+ 要求)
my_bool reconnect = 1;
mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect);

歷史版本陷阱

  • ≤ 5.0.2:默認 reconnect=1,自動重連開啟
  • 5.0.3 - 5.0.18:默認關閉,但 mysql_real_connect 內部會重置 reconnect=0,導致 mysql_options 必須在 連接後 設置
  • ≥ 5.0.19:修復上述問題,統一在 mysql_real_connect 後 設置即可

最佳實踐:統一在 mysql_real_connect 成功後調用 mysql_options

mysql_ping 內部實現

int mysql_ping(MYSQL *mysql) {
  int res = simple_command(mysql, COM_PING, 0, 0, 0);
  if (res == CR_SERVER_LOST && mysql->reconnect) {
    res = simple_command(mysql, COM_PING, 0, 0, 0);  // 重試一次
  }
  return res;
}

simple_command / cli_advanced_command 核心邏輯

  1. 檢查 mysql->net.vio == 0 (VIO層連接斷開) → 調用 mysql_reconnect
  2. 發送命令包 net_write_command
  3. 發送失敗且非封包過大 → end_server 關閉連接 → mysql_reconnect 重連 → 再次發送
  4. 讀取響應包校驗

mysql_reconnect 完整流程

mysql_reconnect():
  1. mysql_close() 釋放舊資源
  2. mysql_init() 重新初始化 MYSQL 結構
  3. 恢復連接參數 (host/user/pass/db/port/unix_socket/client_flag)
  4. mysql_real_connect() 建立新連接
  5. 重新發送會話級命令:
     - SET NAMES charset
     - SET AUTOCOMMIT
     - SET SQL_MODE
     - 等...

關鍵發現與警示

場景自動重連行為風險
mysql_query()✅ 支援 (經 cli_advanced_command)事務隱式回滾、會話變量丟失、臨時表消失
mysql_ping()✅ 支援同上
事務中斷開❌ 不重連 (官方文檔明確)數據不一致
鎖表(LOCK TABLES)❌ 鎖丟失併發衝突

生產環境建議

  1. 關閉自動重連 (reconnect=0),在應用層實現重試邏輯
  2. 應用層重試可感知:事務邊界、會話狀態、業務幂等性
  3. 使用連接池 (HikariCP/Druid) 內建驗證查詢 (SELECT 1) + 超時回收
  4. 設置 wait_timeout / interactive_timeout 大於應用空閒時間
  5. 監控 Aborted_clients / Threads_connected 指標

驗證腳本 (C語言)

// 編譯: gcc -o test_reconnect test_reconnect.c $(mysql_config --cflags --libs)
#include 
#include 

int main() {
  MYSQL *mysql = mysql_init(NULL);
  mysql_real_connect(mysql, "127.0.0.1", "root", "pass", "test", 3306, NULL, 0);
  
  my_bool reconnect = 1;
  mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect);
  
  while (1) {
    if (mysql_query(mysql, "SELECT 1")) {
      fprintf(stderr, "Error %d: %s\n", mysql_errno(mysql), mysql_error(mysql));
    }
    sleep(10);  // 模擬長空閒,手動重啟 mysqld 測試
  }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved