Redis & Advanced Caching

Redis with Laravel

20 min Lesson 25 of 30

Redis with Laravel

Laravel provides first-class support for Redis through elegant APIs including Cache, Redis facade, queues, sessions, and broadcasting. Learn how to leverage Redis to build high-performance Laravel applications.

Laravel Redis Configuration

Configure Redis connections in config/database.php:

config/database.php:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),

'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],

'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],

'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
],
],
.env Configuration:
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0
REDIS_CACHE_DB=1

# Or use URL format
REDIS_URL=redis://user:password@127.0.0.1:6379
Redis Clients:
  • phpredis: PHP extension, faster performance (recommended for production)
  • predis: Pure PHP library, easier installation, no extension required
Install phpredis: pecl install redis && echo "extension=redis.so" > /etc/php/conf.d/redis.ini

Laravel Cache Facade

Use Cache facade with Redis driver for high-performance caching:

config/cache.php:
'default' => env('CACHE_DRIVER', 'redis'),

'stores' => [
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
],
Basic Cache Operations:
use Illuminate\Support\Facades\Cache;

# Store value for 1 hour
Cache::put('key', 'value', now()->addHours(1));

# Store forever (no expiration)
Cache::forever('key', 'value');

# Retrieve value
$value = Cache::get('key');

# Retrieve with default
$value = Cache::get('key', 'default');

# Retrieve and delete
$value = Cache::pull('key');

# Check existence
if (Cache::has('key')) {
// Key exists
}

# Delete key
Cache::forget('key');

# Clear all cache
Cache::flush();
Cache Remember Pattern:
# Retrieve or store if not exists
$users = Cache::remember('users', 3600, function () {
return DB::table('users')->get();
});

# Remember forever
$settings = Cache::rememberForever('settings', function () {
return DB::table('settings')->get();
});

# Increment/Decrement
Cache::increment('counter');
Cache::decrement('counter');
Cache::increment('counter', 5);
Cache::decrement('counter', 5);
Cache Tip: Use Cache::remember() for database queries to automatically cache results and only query the database if cache misses.

Cache Tags (Redis Only)

Cache tags allow you to group related cache items and flush them together:

Using Cache Tags:
# Store with tags
Cache::tags(['users', 'authors'])->put('user:1', $user, 3600);
Cache::tags(['users'])->put('user:2', $user2, 3600);

# Retrieve with tags
$user = Cache::tags(['users'])->get('user:1');

# Flush all items with specific tag
Cache::tags(['users'])->flush();

# Flush multiple tags
Cache::tags(['users', 'authors'])->flush();
Practical Example:
# In UserController
public function index()
{
return Cache::tags(['users', 'index'])->remember('users.all', 3600, function () {
return User::all();
});
}

# After user update, invalidate cache
public function update(Request $request, User $user)
{
$user->update($request->all());
Cache::tags(['users'])->flush();
return redirect()->back();
}

Redis Facade - Direct Redis Commands

Use Redis facade for direct access to Redis commands:

Basic Redis Operations:
use Illuminate\Support\Facades\Redis;

# String operations
Redis::set('name', 'John');
$name = Redis::get('name');

# Hash operations
Redis::hset('user:1', 'name', 'John');
Redis::hset('user:1', 'email', 'john@example.com');
$user = Redis::hgetall('user:1');

# List operations
Redis::lpush('queue:jobs', json_encode($job));
$job = Redis::rpop('queue:jobs');

# Set operations
Redis::sadd('tags', 'php', 'laravel', 'redis');
$tags = Redis::smembers('tags');

# Sorted Set operations
Redis::zadd('leaderboard', 100, 'user:1');
Redis::zadd('leaderboard', 200, 'user:2');
$top = Redis::zrevrange('leaderboard', 0, 9);
Multiple Redis Connections:
# Use default connection
Redis::get('key');

# Use cache connection
Redis::connection('cache')->get('key');

# Define custom connection
$redis = Redis::connection('custom');
$redis->set('key', 'value');

Redis Transactions and Pipelines

Execute multiple commands atomically or in batch:

Transactions (MULTI/EXEC):
Redis::transaction(function ($redis) {
$redis->set('key1', 'value1');
$redis->set('key2', 'value2');
$redis->incr('counter');
});
# All commands execute atomically

Pipelines (Batch Commands):
Redis::pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", "value$i");
}
});
# Commands sent in single network round trip

Rate Limiting with Redis

Laravel provides Redis-based rate limiting for API endpoints and actions:

Throttle Middleware:
use Illuminate\Routing\Middleware\ThrottleRequests;

# In routes/api.php
Route::middleware(['throttle:60,1'])->group(function () {
Route::get('/user', [UserController::class, 'index']);
});
# 60 requests per 1 minute

# Named rate limiter
Route::middleware(['throttle:api'])->group(function () {
// Routes
});
Custom Rate Limiter:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

# In AppServiceProvider
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});

# Custom rate limit per user tier
RateLimiter::for('api', function (Request $request) {
return $request->user()->isPremium()
? Limit::none()
: Limit::perMinute(100);
});
Manual Rate Limiting:
use Illuminate\Support\Facades\RateLimiter;

$executed = RateLimiter::attempt(
'send-email:'.$user->id,
$maxAttempts = 5,
function () use ($user) {
Mail::to($user)->send(new Newsletter());
},
$decaySeconds = 60
);

if (!$executed) {
return 'Too many emails sent!';
}

Queues with Redis

Use Redis as queue driver for background job processing:

config/queue.php:
'default' => env('QUEUE_CONNECTION', 'redis'),

'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
],
],
Dispatch Jobs:
use App\Jobs\ProcessPodcast;

# Dispatch to queue
ProcessPodcast::dispatch($podcast);

# Dispatch to specific queue
ProcessPodcast::dispatch($podcast)->onQueue('processing');

# Delay execution
ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(10));

# Run queue worker
php artisan queue:work redis --queue=high,default,low
Queue Benefits with Redis:
  • Fast job enqueuing and processing
  • Multiple queues with priority
  • Job retry and failure handling
  • Delayed jobs
  • Real-time visibility with Horizon

Broadcasting with Redis

Use Redis for real-time event broadcasting with WebSockets:

config/broadcasting.php:
'default' => env('BROADCAST_DRIVER', 'redis'),

'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
],
Broadcast Events:
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class OrderShipped implements ShouldBroadcast
{
public $order;

public function broadcastOn()
{
return new Channel('orders.' . $this->order->id);
}
}

# Trigger event
event(new OrderShipped($order));

# Listen with Laravel Echo + Socket.io
Echo.channel('orders.1')
.listen('OrderShipped', (e) => {
console.log(e.order);
});

Session Driver with Redis

Store user sessions in Redis for speed and scalability:

config/session.php:
'driver' => env('SESSION_DRIVER', 'redis'),
'connection' => 'default',

.env:
SESSION_DRIVER=redis
SESSION_LIFETIME=120
Session Benefits: Redis sessions are faster than file-based sessions and work seamlessly across multiple servers in load-balanced environments.

Cache Locking (Atomic Operations)

Prevent race conditions with distributed locks:

Cache Locks:
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('process-orders', 10);

if ($lock->get()) {
try {
// Process orders (exclusive access for 10 seconds)
processOrders();
} finally {
$lock->release();
}
} else {
// Another process is already running
return 'Already processing';
}

# Block until lock is available
$lock->block(5); // Wait up to 5 seconds
// Critical section
$lock->release();

Laravel Horizon - Redis Queue Monitoring

Horizon provides a dashboard for monitoring Redis queues:

Install Horizon:
composer require laravel/horizon
php artisan horizon:install
php artisan migrate

# Start Horizon
php artisan horizon

# Access dashboard at /horizon
Route::get('/horizon', function () {
return view('horizon::layout');
});
Horizon Features:
  • Real-time queue metrics
  • Failed job management
  • Job throughput graphs
  • Worker monitoring
  • Auto-scaling workers
  • Job tagging and searching

Best Practices

Laravel + Redis Best Practices:
  • ✅ Use Cache::remember() for database query caching
  • ✅ Use cache tags to group related cache items
  • ✅ Configure separate Redis databases for cache, sessions, queues
  • ✅ Use phpredis extension in production for better performance
  • ✅ Implement rate limiting for API endpoints
  • ✅ Use queues for slow operations (emails, image processing, etc.)
  • ✅ Monitor Redis with Laravel Horizon
  • ✅ Use cache locks to prevent race conditions
  • ✅ Set appropriate TTL values to avoid memory bloat
  • ✅ Use Redis for sessions in multi-server deployments
Exercise: Build a Laravel application with Redis: 1) Configure Redis for cache, session, and queue, 2) Create an API endpoint with rate limiting (60 requests/minute), 3) Cache database queries using Cache::remember(), 4) Create a background job for sending emails, 5) Implement cache tags for user data and flush on update, 6) Monitor queues with Horizon dashboard.