Cache — storing computed or fetched results to avoid repeating the work
Beginner5 min read·eng-20-002
interviewperformance
Concept
Cache — a storage layer that holds copies of expensive-to-compute or expensive-to-fetch data so future requests can be served faster from the copy rather than recomputing.
The caching trade-off: Speed vs freshness. Cached data can be stale. The question is always: "Can I tolerate serving data that's X minutes old?"
What to cache:
- Database query results that don't change often (user counts, product lists, settings).
- Rendered HTML fragments.
- API responses from external services.
- Session data.
- Computed results (analytics, aggregates).
Cache layers in a PHP app:
- OPcache: Caches compiled PHP bytecode. Fast. Automatic. (See eng-20-005.)
- Application cache: Laravel's
Cache::facade. Redis or Memcached. Caches data. - Database query cache: MySQL's query cache (deprecated in MySQL 8). Use Redis instead.
- HTTP cache: Browser cache (
Cache-Controlheaders), CDN cache. - Reverse proxy cache: Nginx or Varnish can cache full HTTP responses.
Cache invalidation: One of the two hard problems in computer science. When underlying data changes, the cache must be invalidated (cleared or updated). Strategies: TTL (time-to-live), event-based invalidation, write-through, cache-aside.
In Laravel: Cache::put(), Cache::get(), Cache::remember(), Cache::forget(). Supports Redis, Memcached, database, file, array drivers.
Code Example
php
<?php
// CACHE DRIVERS in Laravel (config/cache.php)
// 'default' => env('CACHE_DRIVER', 'redis')
// Options: redis, memcached, database, file, array (in-memory, resets per request)
// BASIC cache operations
Cache::put('user:1:profile', $user, now()->addHours(1)); // store for 1 hour
$profile = Cache::get('user:1:profile'); // retrieve
$profile = Cache::get('user:1:profile', 'default value'); // with fallback
Cache::forget('user:1:profile'); // delete
Cache::flush(); // clear all (careful in production!)
// CACHE-ASIDE pattern (most common) — check cache, if miss fetch from DB
public function getUser(int $id): User
{
return Cache::remember("user:{$id}", 3600, function () use ($id) {
return User::with('profile', 'preferences')->findOrFail($id);
});
// If 'user:1' is in cache → return cached, no DB query
// If not → run the closure, cache the result for 3600 seconds, return it
}
// CACHE WITH TAGS — group related items for bulk invalidation
Cache::tags(['users', "user:{$userId}"])->remember('profile', 3600, fn() => User::find($userId));
Cache::tags(['users'])->flush(); // flush all user-tagged caches when users change
// WRITE-THROUGH — update cache when writing to DB
public function updateUser(int $id, array $data): User
{
$user = User::findOrFail($id)->update($data);
Cache::put("user:{$id}", $user, 3600); // update cache immediately
return $user;
}
// HTTP CACHE — let browsers/CDNs cache responses
Route::get('/api/products', function () {
$products = Cache::remember('products', 3600, fn() => Product::all());
return response()
->json($products)
->header('Cache-Control', 'public, max-age=3600')
->header('ETag', md5($products->toJson()));
});