PHP 連接 MySQL 有兩大核心介面:MySQLi (MySQL Improved) 與 PDO (PHP Data Objects)。選擇正確的介面、配置最佳連線參數、實作健全錯誤處理,是資料庫應用穩定性的基石。本文系統性比較兩者架構、提供完整連線代碼模板、解析進階選項與最佳實務。
| 維度 | MySQLi | PDO |
| 支援資料庫 | 僅 MySQL/MariaDB | 12+ (MySQL, PostgreSQL, SQLite, SQL Server, Oracle, ODBC...) |
| API 風格 | 雙重:物件導向 + 程序式 | 純物件導向 |
| 預處理語句 | 原生支援,永真實預處理 | 支援,預設模擬 (需關閉 ATTR_EMULATE_PREPARES) |
| 錯誤處理 | 手動檢查屬性/回傳值 | 例外模式 (ERRMODE_EXCEPTION) |
| 命名參數 | 不支援 (僅 ? 位置) | 支援 (:name) |
| 多重結果集 | 支援 (next_result) | 不直接支援 |
| 異步查詢 | 支援 (poll) | 不支援 |
| 學習曲線 | 中等 (雙 API) | 低 (單一 OO API) |
| 遷移成本 | 高 (綁定 MySQL) | 低 (資料庫無關) |
選擇建議
- 新專案:強烈推薦 PDO — 現代、統一、可移植、例外導向
- 純 MySQL 既有專案:MySQLi 可接受,但新模組建議 PDO
- 需多資料庫支援:PDO 唯一選擇
- 高併發異步需求:MySQLi async 或 Swoole/ReactPHP + PDO
標準連線代碼模板
MySQLi 物件導向 (含完整錯誤處理)
<?php
$host = 'localhost'; $port = 3306; $user = 'user'; $pass = 'pass'; $db = 'dbname';
$mysqli = @new mysqli($host, $user, $pass, $db, $port);
if ($mysqli->connect_errno) {
// 生產環境記錄日誌,不輸出詳細錯誤給用戶
error_log("MySQL 連線失敗 [{$mysqli->connect_errno}]: {$mysqli->connect_error}");
throw new RuntimeException("資料庫連線失敗,請稍後再試");
}
// 設定字元集 (關鍵!)
$mysqli->set_charset('utf8mb4');
// 可選:設定時區
$mysqli->query("SET time_zone = '+00:00'");
// 可選:關閉自動提交 (交易控制)
$mysqli->autocommit(false);
return $mysqli;
MySQLi 程序式
$conn = @mysqli_connect($host, $user, $pass, $db, $port);
if (!$conn) {
error_log("MySQL 連線失敗: " . mysqli_connect_error());
throw new RuntimeException("資料庫連線失敗");
}
mysqli_set_charset($conn, 'utf8mb4');
mysqli_autocommit($conn, false);
return $conn;
PDO (生產環境標準配置)
<?php
$host = 'localhost'; $port = 3306; $db = 'dbname'; $user = 'user'; $pass = 'pass';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;port=$port;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 例外模式
PDO::ATTR_EMULATE_PREPARES => false, // 真實預處理
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 關聯陣列
PDO::ATTR_STRINGIFY_FETCHES => false, // 保持原生型別
PDO::ATTR_PERSISTENT => false, // 非持久連線 (見下文)
PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '+00:00'", // 連線初始化指令
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
return $pdo;
} catch (PDOException $e) {
error_log("PDO 連線失敗 [{$e->getCode()}]: {$e->getMessage()}");
throw new RuntimeException("資料庫連線失敗,請稍後再試");
}
進階連線選項深度解析
持久連線 (Persistent Connections)
| 特性 | 說明 |
| MySQLi | mysqli::real_connect(..., MYSQLI_CLIENT_PERSISTENT) 或 host 前綴 p: |
| PDO | PDO::ATTR_PERSISTENT => true |
| 優點 | 重用連線,減少建立開銷 |
| 缺點 | 連線狀態殘留 (交易、暫存表、變數)、達到 max_connections 限制、難以偵錯、PHP-FPM 工序模型下效益有限 |
| 建議 | 生產環境預設關閉,僅極高頻短連線場景經壓測驗證後啟用 |
SSL/TLS 加密連線 (生產環境強制)
$options[PDO::MYSQL_ATTR_SSL_CA] = '/etc/ssl/mysql/ca.pem';
$options[PDO::MYSQL_ATTR_SSL_CERT] = '/etc/ssl/mysql/client-cert.pem';
$options[PDO::MYSQL_ATTR_SSL_KEY] = '/etc/ssl/mysql/client-key.pem';
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = true;
// DSN 不變,驅動自動協商 SSL
Unix Domain Socket (本機最高效能)
// PDO
$dsn = "mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=$db;charset=$charset";
// MySQLi
$mysqli = new mysqli('localhost', $user, $pass, $db, 0, '/var/run/mysqld/mysqld.sock');
連線超時控制
// PDO (PHP 7.1+)
$options[PDO::ATTR_TIMEOUT] = 5; // 秒,連線階段超時
// MySQLi
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5);
// 查詢超時需 SQL 層面設定
$pdo->exec("SET MAX_EXECUTION_TIME=30000"); // 毫秒,MySQL 5.7.8+
錯誤處理策略
| 策略 | MySQLi | PDO |
| 開發環境 | mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 啟用例外 | ERRMODE_EXCEPTION (預設建議) |
| 生產環境 | try-catch 包裝、自定義例外層級、記錄完整堆疊、用戶端顯示通用訊息 | 同左 |
| 死鎖/鎖等待重試 | 捕獲錯誤碼 1213/1205,指數退避重試 (最多 3 次) | 捕獲 SQLSTATE HY000/40001,同左 |
| 連線中斷重連 | ping() 檢測、重新建立連線、重試邏輯封裝 | 同左 |
連線池與高併發架構
PHP 傳統模式 (PHP-FPM/Apache mod_php) 每請求建立連線,無內建連線池。高併發方案:
- PHP-FPM + 靜態/動態程序管理:調整
pm.max_children、pm.start_servers 匹配 DB max_connections
- 持久連線謹慎使用:配合
mysqli.rollback_on_cached_plink (PHP 8.1+)
- 應用層連線池:Swoole/OpenSwoole/RoadRunner + PDO 協程連線池
- ProxySQL:中介層連線池、讀寫分離、查詢重寫、快取
- 唯讀副本分流:主庫寫入、從庫讀取,應用層或 ProxySQL 路由
安裝驗證
<?php
// 檢查擴展載入
var_dump(extension_loaded('mysqli'));
var_dump(extension_loaded('pdo_mysql'));
// 檢查客戶端庫版本
echo "MySQLi client version: " . mysqli_get_client_info() . "\n";
$pdo = new PDO('mysql:host=localhost', 'user', 'pass');
echo "PDO MySQL client version: " . $pdo->getAttribute(PDO::ATTR_CLIENT_VERSION) . "\n";
總結
PHP 連接 MySQL 的核心決策:選 PDO、用例外模式、關閉模擬預處理、設定 utf8mb4、關閉持久連線、生產環境強制 SSL、統一錯誤處理層。MySQLi 保留給純 MySQL 既有專案或需異步/多重結果集的特殊場景。建立標準化連線工廠類別,封裝配置、重試、監控、審計,是專業 PHP 團隊的基本功。
- 上一篇文章: PHP MySQL 建立資料庫完整指南:CREATE DATABASE、字元集配置、權限與三大介面實作
- 下一篇文章: PHP 與 MySQL 入門:從基礎概念到實戰查詢
Copyright ©
程式碼之家 All Rights Reserved