databases storage and caching
Redis Overview
Redis is an in-memory data store commonly used by PHP applications for caching, queues, sessions, locks, counters, and rate limits. It is fast because it keeps data in memory, but that also means developers must think carefully about expiry, persistence, memory limits, and what happens when Redis is unavailable.
Redis should not be treated as a replacement for the relational database that stores core business records. It is usually supporting infrastructure around the database-backed application.
Common PHP Uses
Redis is often used for:
- Cache entries such as rendered fragments, expensive query results, or API responses.
- Queues and background jobs.
- Session storage shared by multiple PHP servers.
- Rate limiting counters.
- Short-lived locks to prevent duplicate work.
- Pub/sub or event-style coordination in some systems.
Laravel, Symfony, and many PHP libraries can use Redis through configuration, so a developer may work with Redis even without calling Redis commands directly.
Key Names
Good Redis keys are predictable and namespaced. A key should say which application area owns it and what identifier it belongs to.
<?php
declare(strict_types=1);
function userProfileCacheKey(int $userId): string
{
return 'cache:user_profile:' . $userId;
}
echo userProfileCacheKey(42) . PHP_EOL;
// Prints:
// cache:user_profile:42
Avoid vague keys like user or data. They collide easily and are hard to debug.
TTLs
A TTL, or time to live, tells Redis when a key should expire. Most cache keys should have an expiry so stale data does not live forever.
<?php
declare(strict_types=1);
$cacheCommand = [
'command' => 'SETEX',
'key' => 'cache:user_profile:42',
'ttl_seconds' => 300,
'value' => '{"id":42,"name":"Sam"}',
];
print_r($cacheCommand);
// Prints:
// [command] => SETEX
// [ttl_seconds] => 300
The exact PHP client syntax depends on the library, such as phpredis, Predis, Laravel's cache layer, or Symfony Cache.
Cache Misses
Code using Redis cache must handle cache misses. The usual pattern is:
- Try Redis.
- If the value exists, return it.
- If it does not exist, load from the database or source service.
- Store the value in Redis with a TTL.
- Return the value.
<?php
declare(strict_types=1);
function cacheDecision(bool $cacheHit): string
{
return $cacheHit ? 'return cached value' : 'load source and store cache';
}
echo cacheDecision(true) . PHP_EOL;
echo cacheDecision(false) . PHP_EOL;
// Prints:
// return cached value
// load source and store cache
The source of truth should still be clear. For a user profile, that is normally the database, not Redis.
Queues And Sessions
Redis is commonly used as a queue backend. Jobs are pushed into Redis and workers pull them out. That requires operational monitoring: queue length, failed jobs, retry counts, and worker health.
Redis can also store sessions so multiple PHP servers can share login state. Session data is sensitive, so Redis should not be exposed publicly, and access should be protected.
Failure Behaviour
Decide what happens if Redis is down.
For cache, the application may be able to bypass Redis and read from the database. For sessions or queues, Redis failure may be a serious outage. For locks, failure can cause duplicate work if the code falls back carelessly.
Do not hide Redis errors in areas where correctness depends on Redis.
What To Check
Before moving on, make sure you can:
- Explain common Redis uses in PHP applications.
- Name Redis keys clearly and predictably.
- Use TTLs for cache entries.
- Distinguish cache from source-of-truth data.
- Describe cache hit and cache miss behaviour.
- Explain why Redis failure impact depends on how Redis is being used.
Practice
Practice: Design Redis Cache Behaviour
Write a small PHP example that models how a user profile cache should behave with Redis.
Requirements
- Build a predictable Redis key for a user profile.
- Include a TTL value.
- Model cache hit and cache miss decisions.
- Identify the database as the source of truth.
- Include a note about what happens if Redis is unavailable.
Show solution
This solution models the behaviour without requiring a Redis server.
<?php
declare(strict_types=1);
function userProfileCacheKey(int $userId): string
{
return 'cache:user_profile:' . $userId;
}
function userProfileCachePlan(int $userId, bool $cacheHit, bool $redisAvailable): array
{
if (!$redisAvailable) {
return [
'key' => userProfileCacheKey($userId),
'source' => 'database',
'action' => 'skip Redis and load from the database',
'ttl_seconds' => null,
];
}
if ($cacheHit) {
return [
'key' => userProfileCacheKey($userId),
'source' => 'redis',
'action' => 'return cached profile',
'ttl_seconds' => 300,
];
}
return [
'key' => userProfileCacheKey($userId),
'source' => 'database',
'action' => 'load profile, store in Redis, then return it',
'ttl_seconds' => 300,
];
}
$examples = [
userProfileCachePlan(42, true, true),
userProfileCachePlan(42, false, true),
userProfileCachePlan(42, false, false),
];
foreach ($examples as $example) {
echo json_encode($example, JSON_THROW_ON_ERROR) . PHP_EOL;
}
// Prints:
// {"key":"cache:user_profile:42","source":"redis","action":"return cached profile","ttl_seconds":300}
// {"key":"cache:user_profile:42","source":"database","action":"load profile, store in Redis, then return it","ttl_seconds":300}
// {"key":"cache:user_profile:42","source":"database","action":"skip Redis and load from the database","ttl_seconds":null}
For a cache, falling back to the database can be acceptable. For Redis-backed sessions, queues, or locks, Redis being unavailable may need to be treated as an outage instead.