案例現場
- 環境:Linux AS4 + PHP 5 + MySQL 5 + Apache 2
- 現象:
top 顯示 mysqld CPU 長期 100%
- 排除:DDoS、系統負載、硬體故障
根因定位
-- 1. 查看當前連線與執行緒
SHOW PROCESSLIST;
-- 發現大量 Sleep 狀態連線(數百個)
-- 2. 檢查 PHP 代碼
// 錯誤模式:只有 mysql_connect(),無 mysql_close()
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('db');
// 執行查詢...
// 缺少 mysql_close($link);
解決方案
- 補全關閉連線:所有 PHP 腳本結束執行
mysql_close()
- 改用持久連線:
mysql_pconnect() 複用連線,但需配合 wait_timeout 管理
- 配置連線池:PHP-FPM / Apache mod_php 配置最大連線數
驗證效果
修復後 CPU 從 100% 降至 1%~10% 正常區間。
MySQL 核心函數速查(PHP mysql_* 擴展已棄用,建議 PDO/MySQLi)
| 函數 | 用途 | 替代 |
| mysql_connect | 建立連線 | mysqli_connect / new PDO |
| mysql_pconnect | 持久連線 | mysqli_pconnect / PDO::ATTR_PERSISTENT |
| mysql_close | 關閉連線 | mysqli_close / $pdo = null |
| mysql_query | 執行查詢 | mysqli_query / $pdo->query |
| mysql_fetch_assoc | 取關聯陣列 | mysqli_fetch_assoc / $stmt->fetch(PDO::FETCH_ASSOC) |
| mysql_real_escape_string | 跳脫字串 | mysqli_real_escape_string / 預處理 |
| mysql_insert_id | 取自增 ID | mysqli_insert_id / $pdo->lastInsertId |
| mysql_affected_rows | 影響行數 | mysqli_affected_rows / $stmt->rowCount |
| mysql_errno/error | 錯誤碼/訊息 | mysqli_errno/error / $pdo->errorInfo |
| mysql_free_result | 釋放結果集 | mysqli_free_result / 無需(PDO 自動) |
系統級排查清單
| 層面 | 指令/工具 | 關鍵指標 |
| OS | top, vmstat, iostat, pidstat | CPU 使用率、上下文切換、IO wait |
| MySQL 連線 | SHOW PROCESSLIST; SHOW STATUS LIKE 'Threads%'; | Sleep 連線數、活躍查詢、等待鎖 |
| 慢查詢 | pt-query-digest / mysqldumpslow | 高頻慢 SQL、全表掃描、臨時表 |
| InnoDB 狀態 | SHOW ENGINE INNODB STATUS | 鎖等待、死鎖、Buffer Pool 命中率 |
| 配置參數 | SHOW VARIABLES | max_connections、wait_timeout、innodb_buffer_pool_size |
最佳實踐總結
- 應用層:務必關閉連線,使用預處理語句,避免 N+1 查詢
- 連線池:設定
max_connections、wait_timeout、interactive_timeout 合理值
- 監控:部署 Prometheus + mysqld_exporter + Grafana 監控儀表板
- 慢查詢:長期開啟
long_query_time=1,定期 pt-query-digest 分析
- 升級:PHP 7.4+ + MySQLi/PDO,棄用 mysql_* 擴展
- 上一篇文章: MySQL-Proxy 讀寫分離實戰:Lua 腳本路由、連線池管理與高可用架構
- 下一篇文章: MySQL 匯入匯出完整指南:mysqldump、mysqlimport、LOAD DATA、SELECT INTO OUTFILE 實戰
Copyright ©
程式碼之家 All Rights Reserved