Cache trong Laravel: tăng tốc ứng dụng mà không làm sai dữ liệu

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Một truy vấn nặng chạy đi chạy lại cho mỗi người truy cập là sự lãng phí. Cache lưu kết quả đã tính để dùng lại, giúp trang tải nhanh hơn và giảm tải cho database. Nhưng cache sai cách còn nguy hiểm hơn không cache, vì người dùng sẽ thấy dữ liệu cũ.

API cache của Laravel

Laravel cung cấp giao diện thống nhất, bên dưới có thể là file, database, Redis hoặc Memcached. Chọn driver trong biến CACHE_STORE (hoặc CACHE_DRIVER ở bản cũ). Các hàm thường dùng:

Cache::put('key', $value, 600);      // lưu 600 giây$v = Cache::get('key', 'mặc định');Cache::forget('key');$stats = Cache::remember('home.stats', 300, function () {    return Order::whereDate('created_at', today())->sum('total');});

remember là hàm bạn sẽ dùng nhiều nhất: nếu khóa đã có thì trả về luôn, nếu chưa thì chạy hàm, lưu kết quả rồi trả về.

Cái gì nên cache?

Cache hợp với dữ liệu đọc nhiều, thay đổi ít, tốn kém để tính:

  • Danh sách danh mục, cấu hình, menu.
  • Kết quả thống kê, bảng xếp hạng.
  • Phản hồi từ API bên ngoài có giới hạn tốc độ.

Không nên cache dữ liệu phải luôn chính xác từng giây (số dư tài khoản, tồn kho cuối cùng) hoặc dữ liệu riêng của từng người dùng mà không đưa id người dùng vào khóa.

Vấn đề khó nhất: làm mất hiệu lực cache

Có câu nói đùa quen thuộc rằng chỉ có hai việc khó trong khoa học máy tính, một trong số đó là vô hiệu hóa cache. Có ba hướng thường dùng:

  1. Hết hạn theo thời gian (TTL): đơn giản nhất, chấp nhận dữ liệu cũ trong vài phút.
  2. Xóa chủ động khi dữ liệu đổi: gọi Cache::forget() trong observer hoặc sau khi lưu.
  3. Khóa có phiên bản: đưa mốc thời gian cập nhật vào khóa, ví dụ "post.{$post->id}.{$post->updated_at->timestamp}", dữ liệu đổi thì khóa đổi theo.
class PostObserver{    public function saved(Post $post): void    {        Cache::forget('home.latest_posts');    }}

Tag và khóa nguyên tử

Với driver hỗ trợ tag như Redis, bạn nhóm nhiều khóa để xóa cùng lúc:

Cache::tags(['posts'])->put('post.1', $post, 600);Cache::tags(['posts'])->flush();

Khi nhiều request cùng lúc thấy cache trống, tất cả đều chạy truy vấn nặng, hiện tượng "cache stampede". Khóa nguyên tử giúp chỉ một tiến trình được phép tính lại:

$lock = Cache::lock('rebuild-stats', 10);if ($lock->get()) {    try { /* tính lại và lưu */ } finally { $lock->release(); }}

Những cache khác trong Laravel

Ngoài cache dữ liệu, còn có cache cấu hình và route cho production: php artisan config:cache, route:cache, view:cache. Chúng giúp khởi động nhanh hơn, nhưng sau khi cache cấu hình, hàm env() ngoài file config sẽ không còn đọc được file .env, vì vậy hãy đọc biến môi trường qua config().

Ví dụ: cache danh sách bài viết nổi bật

Hãy xem một ví dụ trọn vẹn. Trang chủ hiển thị năm bài viết nổi bật, truy vấn phải sắp xếp theo lượt xem và nạp quan hệ tác giả. Với hàng nghìn lượt truy cập mỗi giờ, không cần tính lại mỗi lần:

public function featured(){    return Cache::remember('posts.featured', now()->addMinutes(10), function () {        return Post::with('author:id,name')            ->orderByDesc('views')            ->take(5)            ->get();    });}

Cache sống mười phút, nghĩa là trong tối đa mười phút người dùng có thể thấy thứ tự cũ, và với trang chủ đó là đánh đổi hoàn toàn chấp nhận được. Nếu muốn dữ liệu mới ngay khi biên tập viên xuất bản, hãy gọi Cache::forget('posts.featured') trong sự kiện xuất bản.

Đo hiệu quả của cache

Đừng cache theo cảm tính. Hãy đo thời gian phản hồi và số truy vấn trước và sau khi thêm cache. Nếu truy vấn vốn đã nhanh, cache chỉ thêm độ phức tạp mà không có lợi ích gì. Cache phát huy khi phép tính tốn kém hoặc lượng truy cập lớn, và luôn nên có kế hoạch xử lý khi cache bị xóa đột ngột, vì ứng dụng của bạn phải vẫn chạy đúng dù cache trống.

Sai lầm thường gặp khi dùng cache

Sai lầm đầu tiên là cache dữ liệu theo người dùng nhưng quên đưa id người dùng vào khóa, dẫn đến việc người này nhìn thấy dữ liệu của người kia. Sai lầm thứ hai là đặt thời gian sống quá dài rồi quên cách xóa, khiến lỗi dữ liệu cũ khó lần ra. Sai lầm thứ ba là coi cache là nơi lưu trữ chính: cache có thể bị xóa bất cứ lúc nào, nên dữ liệu thật phải luôn nằm trong database. Cuối cùng, hãy ghi log tỷ lệ trúng cache nếu có thể, vì một cache hiếm khi được dùng lại chỉ tốn bộ nhớ mà không giúp gì.

Kết luận

Hãy cache có chọn lọc: đo trước để biết chỗ nào chậm, đặt TTL hợp lý, có chiến lược xóa rõ ràng và đặt tên khóa nhất quán. Bắt đầu với Cache::remember cho một truy vấn thống kê nặng nhất của bạn, rồi mở rộng dần.

Khi cần một lộ trình học có hệ thống, bạn có thể tham khảo khóa học trên codebem.com.

author_photo
Nguyễn Đình Cường

0 comment