Dưới đây là hướng dẫn chi tiết cách sử dụng Memcached trong Laravel 12 để cache kết quả truy vấn DB nặng — giúp giảm tải MySQL và tăng tốc độ phản hồi:


✅ Mục tiêu:

Giả sử bạn có truy vấn phức tạp như sau:

$posts = DB::table('posts')
    ->where('status', 'published')
    ->orderBy('views', 'desc')
    ->take(100)
    ->get();

Bạn muốn cache kết quả truy vấn này trong Memcached trong 10 phút.


✅ 1. Đảm bảo Laravel dùng Memcached

.env

CACHE_DRIVER=memcached

Kiểm tra lại php -mmemcached:

php -m | grep memcached

Nếu chưa, cài đặt bằng:

sudo apt install php-memcached
sudo systemctl restart php8.x-fpm

✅ 2. Dùng Cache::remember() để cache truy vấn DB

✨ Cú pháp chuẩn:

$data = Cache::remember('cache_key', $seconds, function () {
    return DB::table('...')->get();
});

✨ Áp dụng thực tế:

use IlluminateSupportFacadesCache;
use IlluminateSupportFacadesDB;

$posts = Cache::remember('top_100_published_posts', 600, function () {
    return DB::table('posts')
        ->where('status', 'published')
        ->orderBy('views', 'desc')
        ->limit(100)
        ->get();
});

✅ Kết quả sẽ được cache trong Memcached trong 600 giây = 10 phút. Sau thời gian đó, truy vấn sẽ chạy lại và nạp cache mới.


✅ 3. Dùng key động (có tham số đầu vào)

$categoryId = 5;

$posts = Cache::remember("top_posts_category_{$categoryId}", 600, function () use ($categoryId) {
    return DB::table('posts')
        ->where('category_id', $categoryId)
        ->where('status', 'published')
        ->orderBy('views', 'desc')
        ->limit(100)
        ->get();
});

✅ 4. Cache mãi mãi (cho dữ liệu gần như không thay đổi)

Cache::rememberForever('about_us_page', function () {
    return DB::table('pages')->where('slug', 'about-us')->first();
});

✅ 5. Xóa cache khi cần

Cache::forget('top_100_published_posts');

✅ 6. Debug: kiểm tra Memcached có hoạt động?

Sử dụng:

echo "stats items" | nc localhost 11211

✅ 7. Gợi ý mở rộng: dùng trong Repository hoặc Service

Tách logic ra để dễ tái sử dụng:

class PostRepository
{
    public function getTopPublished($limit = 100)
    {
        return Cache::remember("top_published_posts_{$limit}", 600, function () use ($limit) {
            return DB::table('posts')
                ->where('status', 'published')
                ->orderBy('views', 'desc')
                ->limit($limit)
                ->get();
        });
    }
}

✅ Kết luận

Tác vụCache tốt không?
Truy vấn SELECT nặng✅ Nên cache
Kết quả phân trang✅ Có thể cache
Truy vấn có tham số✅ Tạo key động
INSERT/UPDATE/DELETE❌ Không cache

✅ 8. Dùng Cache::remember() với Eloquent Query Builder

Eloquent cũng giống như Query Builder, bạn có thể cache như sau:

use IlluminateSupportFacadesCache;
use AppModelsPost;

$posts = Cache::remember('top_posts', 600, function () {
    return Post::where('status', 'published')
        ->orderByDesc('views')
        ->limit(100)
        ->get();
});

✅ 9. Dùng remember với tham số động và query phức tạp

Ví dụ: cache top bài viết theo danh mục, trạng thái, user…

$categoryId = 5;
$status = 'published';
$userId = 3;

$cacheKey = "top_posts_cat_{$categoryId}_status_{$status}_user_{$userId}";

$posts = Cache::remember($cacheKey, 600, function () use ($categoryId, $status, $userId) {
    return Post::where('category_id', $categoryId)
        ->where('status', $status)
        ->where('user_id', $userId)
        ->orderByDesc('views')
        ->limit(100)
        ->get();
});

✅ 10. Dùng tag-based cache (cần Redis hoặc database, không dùng được với Memcached mặc định)

Memcached mặc định không hỗ trợ tag trong Laravel. Nếu bạn cần xóa nhiều key theo nhóm (ví dụ: tất cả cache liên quan đến posts), bạn nên chuyển sang Redis.

Cách chuyển:

Trong .env:

CACHE_DRIVER=redis

Sau đó, bạn có thể làm như sau:

Cache::tags(['posts', 'top'])->remember('top_posts', 600, function () {
    return Post::orderByDesc('views')->take(100)->get();
});

Khi muốn xóa toàn bộ cache posts:

Cache::tags(['posts'])->flush();

✅ 11. Gợi ý dùng với pagination

$page = request()->get('page', 1);
$key = "top_posts_page_{$page}";

$posts = Cache::remember($key, 600, function () {
    return Post::where('status', 'published')->paginate(10);
});

✅ Bạn nên đặt thời gian cache ngắn hơn khi dữ liệu hay thay đổi (ví dụ 60s – 300s).


✅ 12. Gợi ý cache API hoặc toàn view

Bạn có thể cache toàn bộ response HTML hoặc dữ liệu JSON từ controller:

$response = Cache::remember('homepage_view', 600, function () {
    return view('homepage', [
        'posts' => Post::latest()->take(10)->get(),
        'news'  => News::latest()->take(5)->get(),
    ])->render();
});

return response($response);

✅ 13. Cache Builder trong Service/Repository pattern (tái sử dụng)

// PostService.php
public function getCachedPopular($categoryId = null)
{
    $key = 'popular_posts_' . ($categoryId ?: 'all');

    return Cache::remember($key, 600, function () use ($categoryId) {
        $query = Post::where('status', 'published');

        if ($categoryId) {
            $query->where('category_id', $categoryId);
        }

        return $query->orderBy('views', 'desc')->take(50)->get();
    });
}

✅ Kết luận cuối:

Tình huống cần cacheNên dùng gì?
Truy vấn SELECT phức tạp✅ Cache bằng remember
Truy vấn có nhiều biến động✅ Cache ngắn (30s–120s)
Xóa cache theo nhóm (tag)❌ Memcached không hỗ trợ tag
Cache dữ liệu ít thay đổi (about us)rememberForever()
Cache toàn bộ view/jsonrender() rồi cache

✅ 14. Tự động xóa cache khi Model thay đổi (Auto cache invalidation)

Bạn có thể dùng Eloquent Event để tự động xóa cache khi Model bị thay đổi, ví dụ như cập nhật bài viết hoặc xóa bài viết.

➤ Ví dụ: Auto xóa cache khi Post thay đổi

// AppModelsPost.php

protected static function booted()
{
    static::saved(function ($post) {
        Cache::forget('top_posts');
    });

    static::deleted(function ($post) {
        Cache::forget('top_posts');
    });
}

✅ Khi bạn update, create, hoặc delete bài viết, cache top_posts sẽ tự bị xóa, tránh dùng dữ liệu cũ.


✅ 15. Cache toàn bộ Response (Middleware response cache)

Thay vì chỉ cache kết quả query, bạn có thể cache cả response HTML hoặc JSON API, để Laravel không phải chạy Controller nữa.

Cài package hỗ trợ:

composer require spatie/laravel-responsecache

Đăng ký middleware:

// AppHttpKernel.php

protected $middlewareGroups = [
    'web' => [
        ...
        SpatieResponseCacheMiddlewaresCacheResponse::class,
    ],
];

Bật cache trong .env:

RESPONSECACHE_ENABLED=true

Cache toàn bộ response cho route:

use SpatieResponseCacheFacadesResponseCache;

Route::get('/homepage', 'HomeController@index')->middleware('cache.response');

✅ Mỗi lần truy cập route /homepage, toàn bộ response (HTML hoặc JSON) được cache lại (thường là 60 phút).


✅ 16. Cache trong route closure (dùng với API hoặc SSR)

Route::get('/top-posts', function () {
    return Cache::remember('api_top_posts', 600, function () {
        return Post::where('status', 'published')->orderByDesc('views')->limit(50)->get();
    });
});

✅ 17. Dùng cấu trúc cache key tốt để dễ quản lý

Gợi ý cách đặt tên key:

Trường hợpKey mẫu
Truy vấn theo danh mụcposts_cat_{$catId}_page_{$page}
Kết quả JSON APIapi_response_homepage
Cache theo useruser_{$userId}_dashboard_data

➕ Nhờ vậy, bạn dễ xóa cache khi cần:

Cache::forget("user_{$userId}_dashboard_data");

✅ 18. Bonus: Cache kết hợp với Queue và Lazy loading

Ví dụ bạn xử lý query rất nặng, bạn có thể:

  • Đưa nó vào Job chạy ngầm
  • Khi Job hoàn thành thì cập nhật cache
dispatch(new GenerateTopPostsCache());

class GenerateTopPostsCache implements ShouldQueue {
    public function handle() {
        $data = Post::where('status', 'published')->orderByDesc('views')->take(100)->get();
        Cache::put('top_posts', $data, now()->addMinutes(30));
    }
}

✅ 19. Kiểm tra cache có lưu chưa bằng Artisan

Laravel hỗ trợ lệnh kiểm tra cache:

php artisan tinker
>>> Cache::get('top_posts');

✅ 20. Tóm tắt chiến lược cache thông minh

Loại dữ liệuCache như nào
Truy vấn SELECT nặngCache::remember()
Kết quả JSON APICache::remember() hoặc middleware response
Dữ liệu view toàn trangview()->render() + cache
Dữ liệu thường xuyên cập nhậtcache ngắn (30–300s)
Cần xoá theo nhómdùng Redis + tag
Auto clear khi DB thay đổidùng Model::saved() / deleted()
Cache lâu dài (ít khi thay đổi)Cache::rememberForever()