Sau đó, tôi có một bất ngờ dành cho bạn - Đây là một bài kiểm tra hiệu suất nhỏ:
class Seq extends Eloquent {
protected $table = 'helper.seq';
protected $primaryKey = 'i';
}
Route::get('/loop', function () {
$limit = 10000;
$st = microtime(true);
$data = Seq::orderBy('i')->take($limit)->get();
var_dump(microtime(true) - $st);
$st = microtime(true);
foreach ($data as $row) {
$row->i;
}
var_dump(microtime(true) - $st);
$pdo = DB::getPdo();
$st = microtime(true);
$data2 = $pdo
->query("select * from helper.seq order by i limit $limit")
->fetchAll(PDO::FETCH_OBJ);
var_dump(microtime(true) - $st);
$st = microtime(true);
foreach ($data2 as $k => $row) {
if ($k == 0) {
$row->diff = 0;
} else {
$row->diff = $row->i - $data2[$k-1]->i;
}
}
var_dump(microtime(true) - $st);
});
helper.seq
là một bảng chỉ có một cột int và 1 triệu hàng.
Và kết quả là:
0.779045s <- Fetch from DB with Eloquent
1.022058s <- Read Eloquent data (Only one column and do nothing with it)
0.020002s <- Fetch from DB with PDO
0.009999s <- Calculate all diffs in a loop
Vì vậy, "tác động nhỏ đến hiệu suất từ tài hùng biện" là:
- Chậm hơn gần 20 lần so với sử dụng PDO đơn giản và
stdClass
khi tìm nạp dữ liệu từ cơ sở dữ liệu. - Chậm hơn ít nhất 100 lần so với
stdClass
khi đọc thuộc tính / thuộc tính trong một vòng lặp.
Vì vậy, nếu bạn muốn cải thiện hiệu suất, hãy chuyển sang PDO thuần túy khi xử lý lượng lớn dữ liệu hoặc ít nhất là sử dụng Trình tạo mặc định.
Bây giờ bạn vẫn có thể cố gắng thực hiện công việc trong MySQL, nhưng yêu cầu sử dụng Eloquent sẽ không thực hiện được.
Tuy nhiên, bạn có thể thử một phiên bản hỗn hợp - Sử dụng Eloquent để tạo truy vấn, nhưng chuyển đổi nó thành Database\Query\Builder
với getQuery()
.
$fooBars = FooBar::where('type', 'FOO')->orderBy('id')
->getQuery()
->select(['*', DB::raw('coalesce(`value` - @last, 0)'), DB::raw('@last := `value`')])
->get();
Nhưng tôi sẽ luôn tránh sử dụng các biến phiên theo cách này trong mã ứng dụng, vì tôi đã thấy nhiều giải pháp như vậy trả lại kết quả sai / không mong muốn sau khi nâng cấp phiên bản.
Vẫn không thuyết phục? Dưới đây là một số thử nghiệm khác:
Sử dụng các biến phiên trong một truy vấn Eloquent được chuyển đổi thành Database\Query\Builder
:
$st = microtime(true);
$data = Seq::getQuery()
->select(['*', DB::raw('coalesce(i - @last, 0)'), DB::raw('@last := i')])
->orderBy('i')->take($limit)->get();
var_dump(microtime(true) - $st);
// runtime: 0.045002s
Giải pháp PHP sử dụng truy vấn Eloquent được chuyển đổi:
$st = microtime(true);
$data2 = Seq::getQuery()->orderBy('i')->take($limit)->get();
foreach ($data2 as $k => $row) {
if ($k == 0) {
$row->diff = 0;
} else {
$row->diff = $row->i - $data2[$k-1]->i;
}
}
var_dump(microtime(true) - $st);
// runtime: 0.039002
Giải pháp PHP với PDO đơn giản và stdClass
$st = microtime(true);
$data3 = $pdo
->query("select * from helper.seq s1 order by i limit $limit")
->fetchAll(PDO::FETCH_OBJ);
foreach ($data3 as $k => $row) {
if ($k == 0) {
$row->diff = 0;
} else {
$row->diff = $row->i - $data3[$k-1]->i;
}
}
var_dump(microtime(true) - $st);
// runtime: 0.035001s
Giải pháp PHP với PDO thuần túy và mảng phân bổ:
$st = microtime(true);
$data4 = $pdo
->query("select * from helper.seq s1 order by i limit $limit")
->fetchAll(PDO::FETCH_ASSOC);
foreach ($data4 as $k => $row) {
if ($k == 0) {
$row['diff'] = 0;
} else {
$row['diff'] = $row['i'] - $data4[$k-1]['i'];
}
}
var_dump(microtime(true) - $st);
// runtime: 0.027001s
Giải pháp ưa thích của bạn là giải pháp chậm nhất và kém tin cậy nhất. Vì vậy, câu trả lời cho câu hỏi của bạn là một giải pháp tồi cho vấn đề của bạn.