首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

PHP MySQL 查詢超時處理:連線、讀寫超時完整解決方案

     編輯:MySQL 資料庫相關知識

問題背景

PHP mysqli/pdo_mysql 預設僅支援連線超時 (MYSQLI_OPT_CONNECT_TIMEOUT),缺乏查詢執行階段的讀/寫超時控制。高負載下慢查詢會阻塞 PHP 進程,導致 FPM 池耗盡、整站雪崩。

現有連線超時設定

$mysqli = mysqli_init();
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5); // 秒
$mysqli->real_connect('localhost', 'user', 'pass', 'db');

底層原理分析

  • mysqli 擴展封裝 libmysqlclient 的 mysql_options()。
  • libmysqlclient 內建 MYSQL_OPT_READ_TIMEOUT (11) 與 MYSQL_OPT_WRITE_TIMEOUT (12)。
  • PHP 擴展未導出這兩個常數,需自行定義。
  • 實際超時 = 設定值 × 3 (內部重試 2 次 + 本身 1 次)。

解決方案:自定義常數啟用讀寫超時

<?php
// 定義 libmysqlclient 常數
if (!defined('MYSQL_OPT_READ_TIMEOUT'))  define('MYSQL_OPT_READ_TIMEOUT', 11);
if (!defined('MYSQL_OPT_WRITE_TIMEOUT')) define('MYSQL_OPT_WRITE_TIMEOUT', 12);

$mysqli = mysqli_init();
// 設定讀寫超時 3 秒 (實際約 9 秒)
$mysqli->options(MYSQL_OPT_READ_TIMEOUT, 3);
$mysqli->options(MYSQL_OPT_WRITE_TIMEOUT, 1);

$mysqli->real_connect('localhost', 'root', 'root', 'test');

// 測試:sleep(1) 成功,sleep(9) 超時斷開
$mysqli->query('SELECT SLEEP(1)');   // 成功
$mysqli->query('SELECT SLEEP(9)');   // 失敗:MySQL server has gone away

進階方案:毫秒級精確控制

libmysqlclient 僅支援秒級。若需毫秒級,需修改:

  1. 客戶端:mysqli 查詢函數加入定時器 (如 pcntl_alarm 或非阻塞 IO + stream_select)。
  2. 伺服端:修改 MySQL 原始碼 vio 層 poll() / setsockopt(SO_RCVTIMEO/SO_SNDTIMEO) 邏輯,重新編譯 libmysqlclient。

PDO 替代方案

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
    PDO::ATTR_TIMEOUT => 5,           // 連線超時
    PDO::MYSQL_ATTR_INIT_COMMAND => 'SET SESSION MAX_EXECUTION_TIME=3000'  -- MySQL 5.7.8+
]);
// MAX_EXECUTION_TIME 單位毫秒,僅對 SELECT 生效

最佳實踐建議

  1. 應用層:非核心查詢 (點擊數、推薦) 加超時保護,失敗降級不阻塞主流程。
  2. 資料庫層:MAX_EXECUTION_TIME、long_query_time、EXPLAIN 監控慢查詢。
  3. 架構層:讀寫分離、快取 (Redis)、異步隊列削峰。
  4. 監控:PHP-FPM request_terminate_timeout、Nginx proxy_read_timeout 同步設定,防止層層疊加。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved