首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

CodeIgniter 3 解決 MySQL "Too many connections" 問題:關閉持久連線與連線池最佳實踐

     編輯:PHP 相關知識

問題現象

專案運行一段時間後,MySQL 報錯:

SQLSTATE[HY000] [1040] Too many connections
mysqli::real_connect(): (HY000/1040): Too many connections

檢查 SHOW PROCESSLIST 發現大量 Sleep 狀態連線佔用,且未隨請求結束釋放。

根本原因

CI 3 預設資料庫配置 $db['default']['pconnect'] = TRUE 啟用**持久連線** (mysqli::pconnect / PDO::ATTR_PERSISTENT)。持久連線在請求結束時**不關閉**,而是歸還給進程連線池供下一請求複用。但在 PHP-FPM / Apache Prefork 模式下:

  • 每個 Worker 進程維護獨立連線池
  • 連線數 = Worker 數 × 並發請求峰值
  • MySQL max_connections (預設 151) 快速耗盡
  • 連線閒置超過 wait_timeout (預設 8 小時) 才會被 MySQL 主動斷開

解決方案:關閉持久連線

修改 application/config/database.php

$db['default'] = array(
    'dsn'   => '',
    'hostname' => 'localhost',
    'username' => 'your_user',
    'password' => 'your_pass',
    'database' => 'your_db',
    'dbdriver' => 'mysqli',
    'dbprefix' => '',
    'pconnect' => FALSE,  // <=== 關鍵:設為 FALSE
    'db_debug' => (ENVIRONMENT !== 'production'),
    'cache_on' => FALSE,
    'cachedir' => '',
    'char_set' => 'utf8mb4',
    'dbcollat' => 'utf8mb4_unicode_ci',
    'swap_pre' => '',
    'encrypt' => FALSE,
    'compress' => FALSE,
    'stricton' => FALSE,
    'failover' => array(),
    'save_queries' => TRUE
);

效果驗證

  1. 重新部署應用
  2. 執行壓力測試 (ab/wrk/JMeter)
  3. 監控 MySQL:SHOW STATUS LIKE 'Threads_connected'; 應隨請求波動,不再單調遞增
  4. 確認 SHOW PROCESSLIST 無大量 Sleep 連線堆積

為什麼不需要手動呼叫 $this->db->close()?

CI 3 資料庫類別的析構函數 __destruct() 會自動檢查:

// system/database/DB_driver.php 簡化邏輯
public function __destruct() {
    if ($this->conn_id && !$this->pconnect) {
        $this->close(); // 非持久連線自動關閉
    }
}
// close() 內部呼叫 mysqli_close() 或 PDO=null

設定 pconnect = FALSE 後,每個 HTTP 請求結束時,PHP 執行垃圾回收 → 呼叫析構函數 → 自動關閉連線,**完全無需手動干預**。

進階優化:連線參數微調

參數建議值說明
MySQL wait_timeout300 (5分鐘)快速回收閒置連線,防止殭屍連線佔用
MySQL max_connections500-1000根據預估峰值 Worker 數 × 1.5 設定
PHP-FPM pm.max_children依記憶體計算每子進程約 30-50MB,留足系統記憶體
CI db_debugFALSE (生產)避免錯誤頁面洩露 SQL 結構
CI save_queriesFALSE (生產)節省記憶體,除非需要 Profiler

高併發場景替代方案:連線池中間件

若關閉持久連線後仍面臨連線建立開銷過大 (如極高 QPS),建議引入連線池中間件:

  • ProxySQL:MySQL 协议層代理,支援連線多路複用、查詢路由、快取
  • MaxScale:MariaDB 官方代理,功能類似
  • Swoole/RoadRunner + 連線池:常駐記憶體應用模式,單進程維護連線池,完全繞過 PHP-FPM 短連線模型

排查清單:確認問題真正解決

# 1. 確認配置生效
grep -r "pconnect" application/config/

# 2. 監控連線數變化 (持續 10 分鐘)
watch -n 2 "mysql -e \"SHOW STATUS LIKE 'Threads_connected';\""

# 3. 壓力測試
ab -n 10000 -c 100 https://your-site.com/api/test

# 4. 檢查錯誤日誌
tail -f /var/log/mysql/error.log
tail -f application/logs/log-$(date +%Y-%m-%d).php

總結

核心修復:application/config/database.php 設定 'pconnect' => FALSE。
原理:關閉持久連線,讓每個請求結束時自動歸還連線給 MySQL,避免 Worker 進程累積閒置連線耗盡 max_connections。
副作用:每請求多一次 TCP 握手 + TLS 握手 (若啟用 SSL) 開銷 (~1-3ms),絕大多數業務可忽略。
進階:高併發場景配合 ProxySQL 或遷移至 Swoole 常駐模式徹底解決連線開銷問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved