NTM Solutions

Thứ Hai, 14 tháng 9, 2026

📘 LARAVEL 13 — BÀI 38 — CACHE

Cache là một trong những kỹ thuật quan trọng để tăng tốc ứng dụng web.

Thay vì mỗi request đều phải truy vấn Database hoặc thực hiện lại một công việc tốn thời gian, Laravel có thể lưu kết quả vào Cache và sử dụng lại trong những request tiếp theo.

Trong các dự án thực tế, Cache đặc biệt hữu ích với:

  • Danh mục sản phẩm.

  • Danh sách bài viết.

  • Thông tin cấu hình.

  • Thống kê.

  • Dữ liệu được truy vấn thường xuyên.

  • API response.

  • Những phép tính hoặc truy vấn Database tốn thời gian.


1. Cache là gì?

Giả sử Blog CMS của chúng ta có đoạn code:

$categories = Category::active()
    ->withCount('posts')
    ->orderBy('name')
    ->get();

Mỗi lần người dùng truy cập Blog:

Request
   ↓
Controller
   ↓
Database
   ↓
Query categories
   ↓
Render View

Nếu có 1.000 người truy cập, câu query này có thể được thực hiện rất nhiều lần.

Trong khi danh mục Blog có thể chỉ thay đổi vài lần trong ngày.

Ta có thể Cache kết quả:

Request
   ↓
Cache?
 ┌─┴─────────┐
 │           │
Có          Không
 │           │
 ↓           ↓
Cache      Database
 │           │
 └─────┬─────┘
       ↓
     View

Lần đầu:

Request
   ↓
Cache không có
   ↓
Database
   ↓
Lưu kết quả vào Cache
   ↓
Response

Những lần sau:

Request
   ↓
Cache có dữ liệu
   ↓
Lấy từ Cache
   ↓
Response

Mục tiêu của Cache không phải là thay thế Database.

Cache là một lớp lưu trữ tạm thời nằm giữa Application và nguồn dữ liệu.


2. Laravel Cache hoạt động như thế nào?

Laravel cung cấp một API thống nhất cho Cache.

Ta có thể sử dụng:

use Illuminate\Support\Facades\Cache;

Sau đó:

Cache::get('key');

Điểm quan trọng là Controller không cần quan tâm quá nhiều đến Cache đang được lưu ở đâu.

Laravel cung cấp nhiều Cache driver khác nhau.

Ví dụ:

Laravel Application
        │
        ▼
   Cache API
        │
 ┌──────┼─────────┐
 ▼      ▼         ▼
Database Redis  Memcached

Theo tài liệu Laravel, Cache có thể sử dụng các backend phổ biến như Database, Redis, Memcached và một số driver khác.


3. Cấu hình Cache

Cấu hình Cache nằm trong:

config/cache.php

Laravel sử dụng biến môi trường:

CACHE_STORE=database

Tùy cấu hình project, bạn có thể sử dụng Cache store khác.

Ví dụ:

CACHE_STORE=database

hoặc:

CACHE_STORE=redis

Sau khi thay đổi .env, nếu ứng dụng đang Cache configuration thì có thể chạy:

php artisan config:clear

4. Database Cache

Một lựa chọn thuận tiện khi học Laravel là sử dụng Database Cache.

Nếu project chưa có bảng Cache, tạo migration:

php artisan make:cache-table

Sau đó:

php artisan migrate

Laravel sẽ tạo bảng dùng để lưu dữ liệu Cache.

Ví dụ có thể hình dung:

Database

users
categories
posts
cache
cache_locks

Cache Database phù hợp cho môi trường học tập hoặc những ứng dụng chưa cần Redis.


5. Cache::put()

Để lưu một giá trị vào Cache:

use Illuminate\Support\Facades\Cache;

Cache::put('name', 'Laravel 13', 60);

Trong đó:

name
    ↓
Cache Key

Laravel 13
    ↓
Cache Value

60
    ↓
TTL - thời gian sống

Sau 60 giây, Cache hết hạn.

Ta cũng có thể sử dụng DateTime:

Cache::put(
    'name',
    'Laravel 13',
    now()->addMinutes(10)
);

Laravel hỗ trợ cả thời gian tính bằng giây và thời điểm hết hạn cụ thể.


6. Cache::get()

Để lấy dữ liệu:

$value = Cache::get('name');

Nếu Cache tồn tại:

Laravel 13

Nếu Cache không tồn tại:

null

Có thể cung cấp giá trị mặc định:

$value = Cache::get('name', 'Default Value');

Nếu key không tồn tại:

Default Value

7. Cache::has()

Kiểm tra Cache có tồn tại hay không:

if (Cache::has('name')) {
    // Cache tồn tại
}

Ví dụ:

if (Cache::has('categories')) {
    $categories = Cache::get('categories');
}

8. Cache::remember()

Đây là một trong những phương thức quan trọng nhất khi làm Laravel.

Thay vì viết:

if (Cache::has('categories')) {

    $categories = Cache::get('categories');

} else {

    $categories = Category::active()
        ->withCount('posts')
        ->orderBy('name')
        ->get();

    Cache::put(
        'categories',
        $categories,
        3600
    );
}

Ta có thể viết ngắn gọn:

$categories = Cache::remember(
    'categories',
    3600,
    function () {
        return Category::active()
            ->withCount('posts')
            ->orderBy('name')
            ->get();
    }
);

Logic:

Cache::remember()

        │
        ▼
   Cache có key?
      /      \
    Có        Không
    │           │
    ▼           ▼
Return       Closure
cache          ↓
             Database
                ↓
          Lưu vào Cache
                ↓
             Return

Đây là pattern rất phổ biến trong các Laravel project thực tế.


9. Ví dụ Cache Categories trong Blog CMS

Giả sử BlogController đang có:

public function index()
{
    $posts = Post::published()
        ->whereHas('category', fn ($q) => $q->active())
        ->with(['user', 'category'])
        ->latest('published_at')
        ->paginate(10);

    $categories = Category::active()
        ->withCount('posts')
        ->orderBy('name')
        ->get();

    return view('blog.index', compact(
        'posts',
        'categories'
    ));
}

Ta có thể Cache $categories:

use Illuminate\Support\Facades\Cache;

public function index()
{
    $posts = Post::published()
        ->whereHas('category', fn ($q) => $q->active())
        ->with(['user', 'category'])
        ->latest('published_at')
        ->paginate(10);

    $categories = Cache::remember(
        'blog.categories',
        3600,
        function () {
            return Category::active()
                ->withCount('posts')
                ->orderBy('name')
                ->get();
        }
    );

    return view('blog.index', compact(
        'posts',
        'categories'
    ));
}

Ở đây:

'blog.categories'

Cache Key.

3600

là:

3600 giây = 60 phút

10. Cache Key

Cache Key nên được đặt tên rõ ràng.

Ví dụ:

'blog.categories'
'blog.latest_posts'
'blog.popular_posts'
'user.' . $user->id
'post.' . $post->id

Không nên sử dụng những key quá chung chung:

'data'
'list'
'users'

Trong một project lớn, nên tổ chức Cache Key có quy ước.

Ví dụ:

blog.categories
blog.posts.latest
blog.posts.popular

user.1
user.2

post.15
post.16

11. Cache dữ liệu theo ID

Ví dụ lấy User:

$user = Cache::remember(
    'user.' . $id,
    3600,
    function () use ($id) {
        return User::find($id);
    }
);

Hoặc Post:

$post = Cache::remember(
    'post.' . $id,
    3600,
    function () use ($id) {
        return Post::with([
            'user',
            'category'
        ])->findOrFail($id);
    }
);

Đây là cách rất hữu ích đối với những dữ liệu được đọc nhiều nhưng thay đổi ít.


12. Cache Query Database

Cache đặc biệt hữu ích khi Query phức tạp.

Ví dụ:

$posts = Cache::remember(
    'blog.latest_posts',
    600,
    function () {
        return Post::published()
            ->with(['user', 'category'])
            ->latest('published_at')
            ->limit(10)
            ->get();
    }
);

Ở đây Cache sống:

600 giây
= 10 phút

Nếu trong 10 phút có 100 request:

Request 1  → Database
Request 2  → Cache
Request 3  → Cache
Request 4  → Cache
...
Request 100 → Cache

Thay vì:

100 Database Queries

ta có thể giảm xuống gần:

1 Database Query
+
99 Cache Reads

Đây chính là một trong những mục đích quan trọng nhất của Cache.


13. Xóa Cache bằng forget()

Khi dữ liệu thay đổi, đôi khi phải xóa Cache.

Ví dụ:

Cache::forget('blog.categories');

Sau đó request tiếp theo:

Cache không tồn tại
       ↓
Query Database
       ↓
Tạo Cache mới

14. Ví dụ khi cập nhật Category

Giả sử Admin sửa Category:

public function update(Request $request, Category $category)
{
    $validated = $request->validate([
        'name' => ['required', 'string', 'max:255'],
        'slug' => ['required', 'string', 'max:255'],
        'description' => ['nullable', 'string'],
        'is_active' => ['required', 'boolean'],
    ]);

    $category->update($validated);

    Cache::forget('blog.categories');

    return redirect()
        ->route('categories.index')
        ->with('success', 'Cập nhật danh mục thành công.');
}

Điều này rất quan trọng.

Bởi vì nếu:

Category Database
      ↓
đã thay đổi

nhưng:

Cache
      ↓
vẫn chứa dữ liệu cũ

thì Blog có thể tiếp tục hiển thị dữ liệu cũ.


15. Xóa Cache khi tạo Category

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => ['required', 'string', 'max:255'],
        'slug' => ['required', 'string', 'max:255'],
        'description' => ['nullable', 'string'],
        'is_active' => ['required', 'boolean'],
    ]);

    Category::create($validated);

    Cache::forget('blog.categories');

    return redirect()
        ->route('categories.index')
        ->with('success', 'Thêm danh mục thành công.');
}

16. Xóa Cache khi xóa Category

public function destroy(Category $category)
{
    $category->delete();

    Cache::forget('blog.categories');

    return redirect()
        ->route('categories.index')
        ->with('success', 'Xóa danh mục thành công.');
}

Như vậy:

Create Category
       ↓
Forget Cache

Update Category
       ↓
Forget Cache

Delete Category
       ↓
Forget Cache

Đây là nguyên tắc rất quan trọng:

Dữ liệu thay đổi → phải nghĩ đến việc Invalidate Cache.


17. Cache::add()

add() chỉ lưu dữ liệu nếu key chưa tồn tại.

Cache::add(
    'name',
    'Laravel 13',
    3600
);

Nếu:

name chưa tồn tại

thì lưu.

Nếu:

name đã tồn tại

thì không ghi đè.

Phương thức này hữu ích trong một số trường hợp cần tạo giá trị một lần.


18. Cache::increment()

Cache cũng có thể dùng để tăng một giá trị:

Cache::increment('views');

Hoặc:

Cache::increment('views', 5);

Ví dụ:

views = 10

increment()

        ↓

views = 11

19. Cache::decrement()

Tương tự:

Cache::decrement('stock');

Hoặc:

Cache::decrement('stock', 5);

Ví dụ:

stock = 100

decrement(..., 5)

        ↓

stock = 95

Trong những bài toán liên quan đến counter hoặc giới hạn tài nguyên, các thao tác atomic của Cache có thể hữu ích.


20. Cache::pull()

Nếu muốn:

Lấy Cache
   +
Xóa Cache

có thể sử dụng:

$value = Cache::pull('name');

Ví dụ:

Cache::put('coupon', 'ABC123', 600);

$coupon = Cache::pull('coupon');

Sau khi pull():

coupon
   ↓
đã bị xóa

Laravel cũng cung cấp pull() cho trường hợp lấy giá trị rồi xóa khỏi Cache.


21. Cache::flush()

Có thể xóa toàn bộ Cache:

Cache::flush();

Cẩn thận với phương thức này.

flush() không quan tâm prefix Cache của ứng dụng và có thể xóa toàn bộ entries trong cache store đang sử dụng. Vì vậy không nên tùy tiện gọi trong code production nếu Cache store được chia sẻ với ứng dụng khác.

Thông thường khi phát triển:

php artisan cache:clear

cũng có thể được sử dụng để xóa application cache.


22. Cache Helper

Ngoài:

Cache::get(...)

Laravel còn cung cấp helper:

cache()

Ví dụ:

$value = cache('name');

Có thể lưu:

cache([
    'name' => 'Laravel 13'
], 3600);

Hoặc:

$value = cache()->remember(
    'name',
    3600,
    fn () => 'Laravel 13'
);

Laravel cung cấp cả Cache facade và global cache() helper.


23. Cache::flexible()

Laravel cung cấp một kỹ thuật rất hữu ích gọi là:

Stale-While-Revalidate

Thông qua:

Cache::flexible()

Ví dụ:

$value = Cache::flexible(
    'blog.categories',
    [300, 600],
    function () {
        return Category::active()
            ->withCount('posts')
            ->orderBy('name')
            ->get();
    }
);

Hai giá trị:

[300, 600]

có nghĩa:

0 ───────── 300 ───────── 600
     Fresh        Stale

Trong khoảng:

0 → 300 giây

Cache còn fresh.

Trong khoảng:

300 → 600 giây

Laravel có thể trả dữ liệu cũ trước, sau đó đăng ký việc refresh dữ liệu sau response.

Sau:

600 giây

Cache được xem là hết hạn và dữ liệu cần được tính lại ngay.

Đây là cách giảm tình trạng nhiều request cùng lúc phải chờ một tác vụ tạo Cache mới. Laravel mô tả Cache::flexible() chính là implementation cho pattern stale-while-revalidate.


24. Cache::touch() trong Laravel 13

Laravel 13 bổ sung:

Cache::touch()

Mục đích là gia hạn TTL của Cache hiện tại mà không cần lấy giá trị ra rồi lưu lại. Đây là một điểm mới đáng chú ý khi học Cache trên Laravel 13.

Ví dụ:

Cache::touch(
    'blog.categories',
    3600
);

Có thể hiểu:

Cache hiện tại
      ↓
Giữ nguyên value
      ↓
Gia hạn TTL

Thay vì phải:

get()
 ↓
put()

25. Cache Store

Laravel cho phép chọn Cache Store.

Ví dụ:

Cache::store('redis')->get('name');

Hoặc:

Cache::store('database')->get('name');

Điều này cho phép một ứng dụng sử dụng nhiều Cache backend tùy mục đích.

Ví dụ:

Laravel
   │
   ├── database
   │
   ├── redis
   │
   └── memcached

26. Redis Cache

Trong các hệ thống lớn, Redis là một lựa chọn rất phổ biến.

Ví dụ:

CACHE_STORE=redis

Redis có ưu điểm lớn về tốc độ và phù hợp với những ứng dụng cần đọc/ghi Cache với tần suất cao.

Laravel 13 có hỗ trợ Redis thông qua cấu hình Redis của framework.

Có thể hình dung:

Laravel
   │
   ▼
Redis
   │
   ├── Cache
   ├── Session
   ├── Queue
   └── Data

Tuy nhiên, khi học Laravel trên máy local, không nhất thiết phải bắt đầu bằng Redis.

Có thể bắt đầu bằng:

Database Cache

Sau đó khi triển khai VPS hoặc hệ thống lớn:

Redis

27. Cache và Database khác nhau như thế nào?

Đây là vấn đề người mới học Laravel rất dễ nhầm.

Database

Dữ liệu chính thức của hệ thống:

users
posts
categories
orders
products

Cache

Dữ liệu tạm thời để tăng tốc:

cached_categories
cached_posts
cached_statistics

Database có tính:

Persistence

Cache có tính:

Temporary

Ví dụ:

DATABASE

Category
Laravel
PHP
MySQL

Cache:

blog.categories
       ↓
Laravel
PHP
MySQL

Nếu Cache bị xóa:

Không sao.

Ứng dụng có thể lấy lại dữ liệu từ Database và tạo Cache mới.


28. Cache Invalidation

Một trong những vấn đề khó nhất của Cache là:

Khi nào phải xóa Cache?

Ví dụ:

Database
Category = Laravel

Cache:

blog.categories
Category = Laravel

Admin đổi:

Laravel

thành:

Laravel 13

Database:

Laravel 13

nhưng Cache vẫn:

Laravel

Nếu không invalidate Cache:

Database ≠ Cache

Vì vậy cần:

Cache::forget('blog.categories');

Sau đó request tiếp theo tạo Cache mới.


29. Cache trong Blog CMS

Với project Blog CMS đang xây dựng, những dữ liệu phù hợp để Cache có thể là:

Categories

blog.categories

Popular Posts

blog.posts.popular

Latest Posts

blog.posts.latest

Statistics

blog.statistics

User information

user.{id}

Post information

post.{id}

Không nên Cache mọi thứ.

Ví dụ danh sách Post có:

paginate(10)

và thay đổi liên tục thì cần cân nhắc kỹ trước khi Cache.


30. Một ví dụ hoàn chỉnh

Controller:

<?php

namespace App\Http\Controllers;

use App\Models\Category;
use App\Models\Post;
use Illuminate\Support\Facades\Cache;

class BlogController extends Controller
{
    public function index()
    {
        $posts = Post::published()
            ->whereHas(
                'category',
                fn ($query) => $query->active()
            )
            ->with(['user', 'category'])
            ->latest('published_at')
            ->paginate(10);

        $categories = Cache::remember(
            'blog.categories',
            3600,
            function () {
                return Category::active()
                    ->withCount('posts')
                    ->orderBy('name')
                    ->get();
            }
        );

        return view(
            'blog.index',
            compact('posts', 'categories')
        );
    }
}

Khi Blog được truy cập:

Request
   ↓
BlogController@index
   ↓
Posts → Database
   ↓
Categories
   ↓
Cache
   ↓
View

Lần đầu:

Categories
     ↓
Database
     ↓
Cache

Các request sau:

Categories
     ↓
Cache

31. Khi nào nên sử dụng Cache?

Nên cân nhắc Cache khi:

  • Query Database nhiều lần.

  • Dữ liệu ít thay đổi.

  • Dữ liệu được nhiều người đọc.

  • Tính toán dữ liệu tốn CPU.

  • Gọi API bên ngoài tốn thời gian.

  • Thống kê cần thời gian xử lý.

  • Website có lượng truy cập lớn.

Ví dụ:

Category
Site Settings
Popular Posts
Statistics
Exchange Rates
API Response

32. Khi nào không nên Cache?

Không phải dữ liệu nào cũng nên Cache.

Ví dụ:

Thông tin thanh toán
Số dư tài khoản
Dữ liệu thay đổi liên tục
Thông tin cần realtime

Nếu Cache sai cách có thể dẫn tới:

Dữ liệu cũ
     ↓
Người dùng nhìn thấy
     ↓
Logic ứng dụng sai

Vì vậy:

Cache phải được thiết kế dựa trên đặc tính của dữ liệu.


33. Cache không làm Database biến mất

Một sai lầm phổ biến là nghĩ:

Cache = Database nhanh hơn

Không chính xác.

Cache thường đóng vai trò:

Application
     ↓
Cache
     ↓
Database

Cache giúp giảm số lần phải truy cập Database.

Nhưng Database vẫn là nguồn dữ liệu chính của ứng dụng.


34. Cache trong kiến trúc ứng dụng

Một ứng dụng Laravel thực tế có thể có kiến trúc:

                  USER
                   │
                   ▼
                Laravel
                   │
          ┌────────┴────────┐
          │                 │
        Cache             Database
          │
       Redis

Khi dữ liệu đã có trong Cache:

User
 ↓
Laravel
 ↓
Redis
 ↓
Response

Nếu Cache miss:

User
 ↓
Laravel
 ↓
Redis ❌
 ↓
Database
 ↓
Redis ← lưu kết quả
 ↓
Response

Đây là mô hình rất phổ biến trong các hệ thống web hiện đại.


35. Tổng kết

Trong bài này chúng ta đã tìm hiểu:

  • Cache là gì.

  • Cache hoạt động như thế nào.

  • Cấu hình Cache.

  • Database Cache.

  • Cache::put().

  • Cache::get().

  • Cache::has().

  • Cache::remember().

  • Cache Query Database.

  • Cache Key.

  • Cache::forget().

  • Cache::add().

  • Cache::increment().

  • Cache::decrement().

  • Cache::pull().

  • Cache::flush().

  • cache() helper.

  • Cache::flexible().

  • Stale-While-Revalidate.

  • Cache::touch() trong Laravel 13.

  • Cache Store.

  • Redis Cache.

  • Cache Invalidation.

  • Ứng dụng Cache vào Blog CMS.


🎯 BÀI TẬP THỰC HÀNH

Bài tập 1 — Cache Categories

Sử dụng:

Cache::remember()

để Cache:

Category::active()
    ->withCount('posts')
    ->orderBy('name')
    ->get();

Thời gian:

60 phút

Bài tập 2 — Xóa Cache

Trong:

CategoryController

thực hiện:

Cache::forget('blog.categories');

sau khi:

  • Tạo Category.

  • Cập nhật Category.

  • Xóa Category.


Bài tập 3 — Cache Post

Tạo Cache:

post.{id}

và sử dụng:

Cache::remember()

để lấy Post cùng:

user
category

Bài tập 4 — Cache Statistics

Tạo một Cache:

blog.statistics

chứa:

[
    'posts' => ...,
    'categories' => ...,
    'users' => ...,
]

Bài tập 5 — Thử Cache::flexible()

Thử:

Cache::flexible(
    'blog.categories',
    [300, 600],
    function () {
        return Category::active()
            ->withCount('posts')
            ->orderBy('name')
            ->get();
    }
);

Quan sát cách Laravel xử lý dữ liệu trong khoảng:

0 → 300 giây

và:

300 → 600 giây

💡 GHI NHỚ

Database lưu dữ liệu chính.

Cache lưu dữ liệu tạm thời để tăng tốc.

Cache Hit → lấy dữ liệu nhanh từ Cache.

Cache Miss → lấy dữ liệu từ nguồn và tạo Cache mới.

Dữ liệu thay đổi → phải nghĩ đến Cache Invalidation.

Với Blog CMS của chúng ta, một ví dụ điển hình là:

Category
    ↓
Database
    ↓
Cache
    ↓
Blog Sidebar

Thay vì mỗi request đều query:

Category::active()
    ->withCount('posts')
    ->orderBy('name')
    ->get();

ta có thể Cache kết quả và chỉ tạo lại khi Cache hết hạn hoặc khi Category thay đổi.

Đó chính là cách Cache bắt đầu trở thành một phần của kiến trúc ứng dụng thực tế, chứ không đơn thuần là một mẹo tăng tốc.

Bài tiếp theo: Bài 39 — Storage, tìm hiểu cách Laravel quản lý file, disk, storage/app, public, upload, URL và Storage facade.

Không có nhận xét nào:

Đăng nhận xét

Facebook Youtube RSS