Giới thiệu
Trong các dự án Laravel, Eloquent là công cụ ORM mạnh mẽ nhưng nếu không sử dụng đúng cách, các truy vấn có thể gây tải nặng cho cơ sở dữ liệu, làm giảm hiệu năng hệ thống. Bài viết này sẽ giới thiệu năm kỹ thuật thực tiễn giúp bạn tối ưu truy vấn Eloquent, giảm thiểu hiện tượng N+1, giảm thời gian phản hồi và tiết kiệm tài nguyên.
Kỹ thuật 1: Sử dụng eager loading để tránh N+1
Vấn đề N+1
Khi gọi Post::all() và sau đó truy cập $post->comments trong vòng lặp, Laravel sẽ thực hiện một truy vấn cho mỗi bài viết để lấy comment, dẫn đến hàng trăm hoặc hàng ngàn truy vấn phụ.
Giải pháp là sử dụng eager loading với phương thức with() để tải quan hệ cùng lúc.
<?php
$posts = App\Models\Post::with('comments')->get();
foreach ($posts as $post) {
echo $post->title . " - " . $post->comments->count() . " comments";
}
?>Kỹ thuật 2: Chunking dữ liệu lớn
Khi cần xử lý hàng triệu bản ghi, việc tải toàn bộ vào bộ nhớ sẽ gây quá tải. Laravel cung cấp phương thức chunk() để lấy dữ liệu theo từng phần nhỏ.
<?php
App\Models\User::chunk(1000, function ($users) {
foreach ($users as $user) {
// Xử lý mỗi người dùng
$user->update(['processed' => true]);
}
});
?>Với mỗi lần chỉ tải 1000 bản ghi, bộ nhớ được kiểm soát chặt chẽ và thời gian thực thi ổn định.
Kỹ thuật 3: Sử dụng raw query khi cần
Một số truy vấn phức tạp, như tính tổng, trung bình hoặc các phép toán trên nhiều bảng, có thể viết ngắn gọn hơn bằng raw SQL. Laravel cho phép thực thi raw query thông qua DB::select() hoặc whereRaw().
<?php
use Illuminate\Support\Facades\DB;
$results = DB::select(
'SELECT u.id, u.name, COUNT(p.id) AS post_count FROM users u '
. 'LEFT JOIN posts p ON p.user_id = u.id '
. 'GROUP BY u.id, u.name HAVING post_count > ?', [10]
);
foreach ($results as $row) {
echo "{$row->name} có {$row->post_count} bài viết\n";
}
?>Kỹ thuật 4: Đánh chỉ mục (index) và tối ưu schema
Việc tạo chỉ mục trên các cột thường được dùng trong điều kiện WHERE, JOIN hoặc ORDER BY giúp CSDL nhanh hơn đáng kể. Khi tạo migration, bạn có thể thêm chỉ mục như sau:
<?php
Schema::table('posts', function (Blueprint $table) {
$table->index('user_id'); // Index cho khóa ngoại
$table->index(['status', 'created_at']); // Composite index
});
?>Đồng thời, tránh lưu trữ dữ liệu dư thừa, sử dụng kiểu dữ liệu phù hợp (ví dụ tinyInteger cho trạng thái) để giảm kích thước bảng.
Kỹ thuật 5: Cache kết quả truy vấn
Đối với các truy vấn không thay đổi thường xuyên, việc lưu vào cache sẽ giảm tải CSDL. Laravel hỗ trợ cache query thông qua phương thức remember() hoặc Cache::remember().
<?php
use Illuminate\Support\Facades\Cache;
$popularPosts = Cache::remember('popular_posts', now()->addMinutes(30), function () {
return App\Models\Post::withCount('comments')
->orderBy('comments_count', 'desc')
->take(10)
->get();
});
foreach ($popularPosts as $post) {
echo $post->title . " - " . $post->comments_count . " comments\n";
}
?>Cache được lưu trong driver cấu hình (Redis, Memcached, file...), giúp truy vấn tiếp theo trả về ngay mà không cần thực hiện lại SQL.
Kết luận
Áp dụng các kỹ thuật trên sẽ giúp bạn giảm thiểu số lượng truy vấn, tối ưu thời gian phản hồi và nâng cao khả năng mở rộng của ứng dụng Laravel. Để nắm vững toàn bộ quy trình phát triển và tối ưu Laravel, Tham khảo khóa học "Lập trình web PHP & MySQL với Laravel Framework" tại đây.








