核心差異
| 特性 | 緩衝查詢 | 非緩衝查詢 |
| 記憶體佔用 | PHP 進程一次性載入全部結果集 | MySQL 端保留,PHP 逐行取回 |
| 結果集導航 | 可隨意移動指標(data_seek、num_rows) | 僅能順向讀取(fetch) |
| 並發限制 | 查詢結束即釋放伺服器資源 | 未讀完前無法在同連線發送新查詢 |
| 適用場景 | 結果集較小、需隨機存取、需知筆數 | 大結果集、串流處理、記憶體受限 |
mysqli 介面
// 緩衝(預設)
$res = $mysqli->query("SELECT * FROM huge_table"); // MYSQLI_STORE_RESULT
$res = $mysqli->query("SELECT * FROM huge_table", MYSQLI_STORE_RESULT);
$stmt->store_result(); // 預處理語句轉緩衝
// 非緩衝
$res = $mysqli->query("SELECT * FROM huge_table", MYSQLI_USE_RESULT);
$res = $mysqli->use_result();
// 預處理預設即為非緩衝,需呼叫 store_result() 轉緩衝
PDO 介面
// 緩衝(預設)
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
$res = $pdo->query("SELECT * FROM huge_table");
// 非緩衝
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$res = $pdo->query("SELECT * FROM huge_table");
// 非緩衝下 $res 是 PDOStatement,需 fetch() 逐行
記憶體考量:libmysqlclient vs mysqlnd
- libmysqlclient:結果集記憶體在 C 層,
memory_limit 不計入,直到賦值給 PHP 變數
- mysqlnd (PHP 5.4+ 預設):結果集記憶體在 PHP 記憶體管理內,
memory_limit 可正確限制
預處理語句特殊行為
$stmt = $mysqli->prepare("SELECT * FROM logs WHERE level = ?");
$stmt->bind_param('s', $level);
$stmt->execute();
// 預設非緩衝:結果留在 MySQL,fetch() 逐行取回
// 需知總筆數或隨機存取時:
$stmt->store_result(); // 一次性拉回 PHP
echo $stmt->num_rows; // 此時可用
// 用完釋放
$stmt->free_result();
// 取得結果集物件(需 mysqlnd)
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
選擇決策樹
- 結果集 < 10MB 且需
num_rows/隨機存取 → 緩衝
- 結果集巨大(GB 級)、串流處理(CSV 匯出、ETL)、記憶體受限 → 非緩衝
- 非緩衝模式下:必須 讀完所有行或呼叫
free_result() 才能在同連線執行下一條 SQL
- 長時間運行腳本(Worker、CLI)偏好非緩衝 + 批次處理,避免記憶體洩漏
效能觀測建議
- 啟用
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) 捕獲錯誤
- 使用
memory_get_peak_usage(true) 監控 PHP 記憶體峰值
- MySQL 端
SHOW PROCESSLIST 觀察 Sending data 狀態持續時間