0

Cache drivers — file, database, Redis, Memcached, array

Beginner5 min read·lv-20-001
interview

Concept

Cache::put, get, remember, forever, forget are the core cache operations. Understanding the remember pattern is the most important — it's the idiomatic way to use cache in Laravel.

The remember pattern:

php
$value = Cache::remember($key, $ttl, fn() => expensiveOperation());

This is cache-aside caching: read from cache, and if missing, compute and store. The closure only executes on cache misses. This is the correct pattern for 90% of Laravel caching.

Key design: Cache keys must be unique and descriptive. Use separators for namespacing: "user:{$id}:profile", "posts:featured:page:{$page}". For cache invalidation, the key must deterministically map to the cached data.

remember vs rememberForever: rememberForever stores indefinitely — but the cache driver may still evict entries under memory pressure (Redis with maxmemory-policy). Never rely on rememberForever for data that MUST be available — it's a performance cache, not a persistent store.

Cache invalidation: The hardest problem in caching. Strategies:

  1. TTL-based: Let entries expire naturally. Stale data within the TTL window is acceptable.
  2. Manual invalidation: Cache::forget($key) after an update. Must invalidate all related keys.
  3. Tag-based: Cache::tags(['posts'])->flush() invalidates all tagged entries at once.

Race conditions: Between "check cache miss" and "store result", another request may also find a miss and compute the value — both store the same value, wasting work. Use atomic locks to prevent cache stampedes.

Code Example

php
<?php
use Illuminate\Support\Facades\Cache;

// remember — the standard pattern
$settings = Cache::remember('app:settings', 3600, function() {
    return Setting::all()->pluck('value', 'key')->toArray();
});

// Keyed by resource ID
$user = Cache::remember("user:{$userId}:profile", 1800, function() use ($userId) {
    return User::with('profile', 'roles')->findOrFail($userId);
});

// Keyed by request parameters (pagination)
$key = "posts:page:{$page}:per:{$perPage}:sort:{$sort}";
$posts = Cache::remember($key, 300, fn() => Post::published()->paginate($perPage));

// Cache invalidation — always forget the correct keys
class PostController extends Controller
{
    public function update(Request $request, Post $post)
    {
        $post->update($request->validated());

        // Invalidate all related cache entries
        Cache::forget("post:{$post->id}");
        Cache::forget("posts:page:1:per:15:sort:latest"); // known page keys
        Cache::tags(['posts'])->flush(); // or: flush all post-tagged cache

        return redirect()->route('posts.show', $post);
    }
}

// TTL options
Cache::put('key', $value, 3600);              // 3600 seconds
Cache::put('key', $value, 60 * 60);           // 1 hour
Cache::put('key', $value, now()->addHour());   // Carbon instance
Cache::put('key', $value, new \DateInterval('PT1H')); // DateInterval

// Flexible cache key generation
function cacheKey(string $prefix, array $params = []): string
{
    ksort($params); // normalize key order
    return $prefix . ':' . md5(serialize($params));
}
$key = cacheKey('users:query', ['role' => 'admin', 'page' => 2, 'sort' => 'name']);
$users = Cache::remember($key, 300, fn() => User::where('role', 'admin')->paginate(15));