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 = true
fetch()
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 = false
fetch()
.👉 Ư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