當使用 mysql_query 或 PDO 預設模式查詢超大結果集時,常遇到 Allowed memory size exhausted 致命錯誤。本文深入分析原因,並提供 PDO 無緩衝查詢的完整解決方案。
傳統 mysql_query 與 PDO 預設行為皆為緩衝查詢:一次性將所有結果集載入記憶體。當資料量達到千萬筆,記憶體佔用呈指數級增長,輕易突破 memory_limit 限制。
MySQL 提供 mysql_unbuffered_query,PDO 對應 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false。核心差異:
| 特性 | 緩衝查詢 | 無緩衝查詢 |
|---|---|---|
| 記憶體佔用 | 載入全部結果集 | 逐行處理,記憶體恆定 |
| 資料列計數 | 支援 num_rows | 不支援 |
| 同一連線並發 | 支援多查詢 | 需處理完或重新連線 |
| 適用場景 | 小/中型結果集 | 大數據匯出、ETL、報表產生 |
function selectBuffered() {
$pdo = new PDO("mysql:host=localhost;dbname=test", 'root', '123456');
$sth = $pdo->prepare('SELECT * FROM large_table');
$sth->execute();
echo '初始記憶體:' . memory_get_usage() . "\n";
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
// 處理邏輯
}
// 記憶體持續飆升,最終 OOM
}
function selectUnbuffered() {
$pdo = new PDO("mysql:host=localhost;dbname=test", 'root', '123456');
// 關鍵設定:停用緩衝
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$sth = $pdo->prepare('SELECT * FROM large_table');
$sth->execute();
echo '初始記憶體:' . memory_get_usage() . "\n";
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
// 記憶體維持在 145KB 左右,極度穩定
processRow($row);
}
}
execute() 瞬間記憶體飆升至 200MB+,拋出 Fatal error: Allowed memory size of 134217728 bytes exhaustedLIMIT offset, count 分批處理,兼顧記憶體與並發掌握無緩衝查詢技巧,讓 PHP 輕鬆應對百萬級甚至億級資料處理任務,不再受限於記憶體瓶頸。