MySQL 憑藉輕量、跨平台、多線程、開源等特性,成為 PHP Web 開發首選數據庫。雖然現代框架多封裝 PDO/Eloquent,但掌握原生 mysql_* / mysqli_* 函數對維護舊專案、理解底層行為、排查疑難雜症至關重要。本文系統整理核心 API、連接模式、錯誤處理與安全規範。
mysqli_connect() | 建立連接 | host, user, pass, db, port, socket | 連接對象 / false | 預設端口 3306,長連接用 mysqli_pconnect() |
| mysqli_select_db() | 選擇默認數據庫 | link, db_name | bool | 連接時可直接指定 db 參數省略此步 |
| mysqli_query() | 執行 SQL | link, query, mode | 結果集對象 / bool | SELECT 返回結果集,DML 返回 bool,**需嚴格檢查 false** |
| mysqli_fetch_assoc() | 取關聯陣列 | result | 陣列 / false | 僅移動指針一行,配合 while 迴圈遍歷 |
| mysqli_fetch_row() | 取索引陣列 | result | 陣列 / false | 性能略優於 assoc,但可讀性差 |
| mysqli_fetch_array() | 取混合陣列 | result, mode | 陣列 / false | MYSQLI_BOTH 佔用雙倍內存,大結果集慎用 |
| mysqli_num_rows() | 獲取行數 | result | int | 僅緩衝查詢有效,非緩衝需先 mysqli_store_result() |
| mysqli_num_fields() | 獲取列數 | result | int | 配合 mysqli_fetch_field() 獲取元數據 |
| mysqli_free_result() | 釋放結果集 | result | void | **長腳本必須顯式調用**,防止內存洩漏 |
| mysqli_close() | 關閉連接 | link | void | 非持久連接腳本結束自動關閉,持久連接需手動管理 |
| mysqli_error() | 獲取錯誤信息 | link | string | **生產環境禁用直接輸出**,記錄日誌並返回友好提示 |
| mysqli_errno() | 獲取錯誤代碼 | link | int | 1062(重複鍵)、1452(外鍵失敗) 等常見代碼需熟記 |
$link = mysqli_connect('127.0.0.1', 'user', 'pass', 'db', 3306);
if (!$link) die('連接失敗: ' . mysqli_connect_error());
// 業務邏輯...
mysqli_close($link);
✅ 簡單、無狀態、適合低併發、PHP-FPM 模式下自動回收。
mysqli_pconnect)$link = mysqli_pconnect('127.0.0.1', 'user', 'pass', 'db');
// 進程池複用連接,不自動關閉
⚠️ 風險:連接數易達 max_connections 上限;事務狀態殘留導致髒讀;長查詢阻塞整個進程池。**高併發 Web 環境強烈不建議使用**,改用連接池中間件(ProxySQL、MaxScale)。
<?php
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
if ($mysqli->connect_errno) {
error_log("DB Connect Failed: {$mysqli->connect_error}");
exit('服務暫時不可用');
}
$mysqli->set_charset('utf8mb4'); // 關鍵:防注入、支援 Emoji
// 1. Prepared Statement 查詢
$stmt = $mysqli->prepare("SELECT id, name, email FROM users WHERE status = ? AND created_at > ?");
$stmt->bind_param('is', $status, $date); // i=integer, s=string, d=double, b=blob
$status = 1;
$date = '2023-01-01';
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo "{$row['name']} <{$row['email']}>\n";
}
$stmt->close();
// 2. 事務處理
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("INSERT INTO orders (user_id, total) VALUES (?, ?)");
$stmt->bind_param('id', $uid, $total);
$stmt->execute();
$order_id = $mysqli->insert_id;
$stmt = $mysqli->prepare("INSERT INTO order_items (order_id, product_id, qty) VALUES (?, ?, ?)");
$stmt->bind_param('iii', $order_id, $pid, $qty);
foreach ($items as $item) {
$pid = $item['pid']; $qty = $item['qty'];
$stmt->execute();
}
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
error_log("Order Failed: {$e->getMessage()}");
}
$mysqli->close();
?>
SET NAMES utf8mb4 或 $mysqli->set_charset('utf8mb4'),否則 GBK 雙字節注入、Emoji 存儲失敗。display_errors=Off,統一記錄 error_log。if (!$result) { log_error($mysqli->error); return false; } 每次查詢必加。SELECT * FROM huge_table 直接 OOM。使用 LIMIT 分頁或 mysqli_use_result() 非緩衝模式逐行處理。"WHERE name = '$name'",**永遠使用預處理綁定參數**。mysqli_free_result()、mysqli_close()。SELECT,INSERT,UPDATE,DELETE,**無 DDL、GRANT、FILE 權限**。mysql_*,PHP 7.0 移除。存量專案優先改用 mysqli 面向過程風格(語法最接近),再逐步重構為面向對象/PDO。phpstan + php-cs-fixer 規則集可批量檢測/修復舊 API 調用。