許多開發者在使用 Linux 虛擬主機(PHP + MySQL 架構)時,都曾遭遇「Can't connect to MySQL server」的錯誤訊息。這類問題往往具有間歇性、難以重現的特徵,讓人抓狂不已。本文將從實戰案例出發,層層剖析連線失敗的核心成因,並提供可落地的優化策略。
一位開發者分享了其真實經歷:網站上線一年運作正常,半年前開始每週出現 1-2 次連線失敗,需聯繫主機商重啟 MySQL 服務才能恢復。錯誤日誌顯示:
Warning: mysql_connect(): Can't connect to MySQL server on 'xx.xx.xx.xx' (4)
主機商聲稱同主機其他網站正常,將責任推給程式碼。開發者嘗試將 mysql_connect() 改為持久連線 mysql_pconnect(),問題緩解半個月,但流量回升後錯誤頻率暴增至每天多次,甚至重啟服務也無效。
虛擬主機環境下,MySQL 的 max_connections 參數通常受限(預設 100-151)。若程式碼未妥善釋放連線,或單頁查詢過多、迴圈中重複查詢,極易耗盡連線池。錯誤代碼 (4) 往往對應 CR_CONN_HOST_ERROR,實際上可能是伺服器拒絕新連線的偽裝表現。
使用 IP 或域名作為 Host 而非 localhost,會強制連線經過 TCP/IP 協議棧,引入 DNS 解析延遲與網關故障風險。若改用 localhost,MySQL 客戶端庫會自動嘗試 Unix Socket 連線,繞過網路層,顯著降低失敗率。
mysql_pconnect() 重用連線雖減少握手開銷,但若連線處於睡眠狀態超過 wait_timeout(預設 8 小時),伺服器會主動斷開,導致下次請求拿到失效連線。流量驟增時,殭屍連線佔用槽位,加劇資源爭搶。
// 錯誤示範
$host = '192.168.1.100'; // 或域名
// 正確示範
$host = 'localhost'; // 自動走 Unix Socket
// 或明確指定 socket 路徑
$host = 'localhost:/var/run/mysqld/mysqld.sock';
JOIN 或 IN 子查詢一次性獲取mysqli 或 PDO 的持久連線池特性| 參數 | 建議值 | 說明 |
|---|---|---|
| max_connections | 300-500 | 視記憶體而定,每連線約 2-3MB |
| wait_timeout | 60-300 | 縮短閒置連線存活時間,釋放槽位 |
| interactive_timeout | 60-300 | 同步調整 |
| max_user_connections | 50-100 | 限制單用戶佔用,防單站拖垮全庫 |
# my.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
定期分析 mysqldumpslow -s t /var/log/mysql/slow.log,針對性加索引、改寫 SQL。
mysql_ping() 心跳檢測,極易拿到斷開的連線SET NAMES utf8mb4 會增加 RTT,建議在連線字串指定 charset=utf8mb4MySQL 連線失敗多為「資源耗盡」與「網路路徑不佳」的疊加效應。優先改用 localhost + Socket 連線、重構高頻頁面查詢邏輯、推動主機商調整核心參數,三管齊下可解決 90% 以上案例。若虛擬主機限制過多,遷移至雲資料庫(如 RDS、Cloud SQL)獲得獨享資源與自動運維,才是長久之計。