PHP cookbook · Beginner
Cache expensive queries with Redis in PHP
Speed up a slow product listing query with Redis while keeping data reasonably fresh.
Approach
Implement cache-aside: read from Redis; on a miss, compute from the database and store with a TTL (plus jitter). Invalidate keys when data changes, and use a short lock to prevent a stampede when a hot key expires.
Solution
<?php
declare(strict_types=1);
final class Cache
{
public function __construct(private \Redis $redis) {}
public function remember(string $key, int $ttl, callable $compute): mixed
{
$cached = $this->redis->get($key);
if ($cached !== false) {
return unserialize($cached, ['allowed_classes' => false]);
}
// Only one process recomputes; others briefly wait and retry
$lock = $this->redis->set("lock:{$key}", '1', ['nx', 'ex' => 10]);
if (!$lock) {
usleep(100_000);
return $this->remember($key, $ttl, $compute);
}
try {
$value = $compute();
$this->redis->setex($key, $ttl + random_int(0, (int) ($ttl * 0.1)), serialize($value));
return $value;
} finally {
$this->redis->del("lock:{$key}");
}
}
}
$products = $cache->remember('products:featured:v2', 300, fn () => $repo->featured(limit: 20));
// After an admin edits featured products:
$redis->del('products:featured:v2');
Pitfalls
- Caching per-user data under shared keys.
unserialize()of untrusted data with objects allowed — disable classes or use JSON.- Infinite waiting on locks if the computing process crashes (locks need TTLs).
Variations
- Use APCu for per-server caches of config-like data.
- Laravel:
Cache::remember()/Cache::flexible()for stale-while-revalidate.
Further reading: Redis patterns
Reference: https://redis.io/docs/latest/develop/use/patterns/
More PHP recipes
- Add CSRF protection to PHP forms
- Add health check endpoints to a PHP app
- Add structured logging with request IDs in PHP
- Build a secure password reset flow in PHP
- Call external APIs with timeouts and retries in PHP
- Handle file uploads securely in PHP
- Implement rate limiting in a PHP API
- Make a PHP payment endpoint idempotent