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'
là 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