Khi bạn query dữ liệu MySQL bằng PDO, mặc định PHP sẽ buffer toàn bộ kết quả vào RAM.
Cờ điều khiển hành vi này là:
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
Cờ này quyết định cách MySQL và PHP trao đổi dữ liệu:
| Chế độ | Mặc định | Ý nghĩa |
|---|---|---|
true | ✅ Mặc định | Toàn bộ kết quả query được tải vào RAM trước khi bạn đọc |
false | ❌ Tắt | Dữ liệu được stream dần, không lưu toàn bộ vào RAM |
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY = truefetch() hoặc foreach() bao nhiêu lần tùy thích.👉 Ưu điểm:
fetch().👉 Nhược điểm:
BLOB (ảnh, nhị phân) cũng bị cache toàn bộ trong RAM, dù bạn không dùng.PDO::MYSQL_ATTR_USE_BUFFERED_QUERY = falsefetch().👉 Ưu điểm:
LONGTEXT, LONGBLOB).SELECT *.👉 Nhược điểm:
fetch() xong.rowCount() trước khi đọc hết dữ liệu.$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, // mặc định
]);
$stmt = $pdo->query("SELECT * FROM big_table");
foreach ($stmt as $row) {
// Mọi dữ liệu đã nằm trong RAM
}
🧩 Toàn bộ kết quả sẽ nạp vào RAM ngay sau khi query() chạy.
Nếu bảng có 500MB dữ liệu → PHP RAM tăng đột ngột.
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false,
]);
$stmt = $pdo->query("SELECT * FROM big_table");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// Dòng nào đọc, dòng đó đến
process($row);
}
// Đảm bảo giải phóng bộ nhớ và cho phép query khác
$stmt->closeCursor();
💡 Mẹo:
closeCursor() bắt buộc nếu bạn muốn chạy query tiếp theo.| Dữ liệu | Buffered | Unbuffered |
|---|---|---|
| 1.000 dòng, mỗi dòng 2MB blob | ⚠️ 2GB RAM | ✅ 5–10MB RAM |
| 100.000 dòng, text nhỏ | ✅ nhanh hơn | chậm hơn 5–10% |
| Query đồng thời (2 query / 1 conn) | ✅ có thể | 🚫 không thể |
| Dữ liệu BLOB, JSON, LONGTEXT | ❌ tốn RAM | ✅ tối ưu |
CI3 mặc định khi dùng:
'dbdriver' => 'mysqli'
→ luôn bật buffered query (và không thể tắt).
Muốn kiểm soát PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, bạn phải dùng:
'dbdriver' => 'pdo'
File: application/config/database.php
$db['default'] = array(
'dsn' => 'mysql:host=localhost;dbname=test;charset=utf8mb4',
'hostname' => '',
'username' => 'root',
'password' => '',
'database' => 'test',
'dbdriver' => 'pdo',
'pconnect' => FALSE,
'db_debug' => TRUE,
'cache_on' => FALSE,
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_unicode_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE,
// ⚙️ Cấu hình tùy chọn PDO
'options' => array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false // Tắt buffered query
)
);
⚠️ Nếu bạn dùng
'mysqli', trường'options'sẽ bị bỏ qua.
Bạn có thể kiểm tra trực tiếp:
var_dump($this->db->conn_id->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY));
Kết quả:
bool(false)
hoặc
bool(true)
$query = $this->db->query("SELECT * FROM tbl_files");
$rows = $query->result_array(); // toàn bộ BLOB nạp vào RAM
$query = $this->db->query("SELECT * FROM tbl_files");
while ($row = $query->unbuffered_row('array')) {
process($row);
}
Lưu ý: CI3 có hỗ trợ
unbuffered_row(), nhưng vẫn cần PDO mode unbuffered để thực sự tiết kiệm RAM.
| Tình huống | Nên tắt |
|---|---|
| Dữ liệu nhỏ, query nhanh (0.001s) | ❌ Giữ nguyên |
| Query chứa BLOB, LONGTEXT | ✅ Nên tắt |
| Duyệt dữ liệu hàng triệu dòng (export, đồng bộ) | ✅ Nên tắt |
| Cần chạy nhiều query song song | ❌ Không tắt |
| Ứng dụng cần tối ưu RAM VPS | ✅ Rất nên tắt |
| Tiêu chí | Buffered = true | Buffered = false |
|---|---|---|
| RAM | Cao (toàn bộ kết quả vào bộ nhớ) | Thấp (stream từng dòng) |
| Tốc độ truy cập | Nhanh (vì cache sẵn) | Hơi chậm hơn |
| Dữ liệu lớn (BLOB, LONGTEXT) | Dễ tràn bộ nhớ | An toàn |
| Nhiều query song song | Có thể | Không thể |
| CI3 hỗ trợ | Mặc định | Cần pdo driver |
pdo và tắt buffered.PDO::MYSQL_ATTR_USE_BUFFERED_QUERY = false là “cứu tinh”.$db['default'] = [
'dsn' => 'mysql:host=localhost;dbname=bigdata;charset=utf8mb4',
'username' => 'root',
'password' => '',
'dbdriver' => 'pdo',
'pconnect' => FALSE,
'db_debug' => TRUE,
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_unicode_ci',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false
]
];
🔸
true: nhanh hơn, tốn RAM hơn
🔸false: tiết kiệm RAM, chậm hơn chút, chỉ một query tại một thời điểm
🔸 CI3 chỉ dùng được nếu chuyển sangdbdriver = 'pdo'
🔸 Nên tắt nếu bạn xử lý dữ liệu lớn hoặc chứa BLOB