Laravel Performance Optimization: Advanced Techniques & Monitoring
Laravel Performance Optimization: Advanced Techniques & Monitoring
Expert Guide by Alaa Amer – Professional Web Developer & Applications Designer
Laravel performance optimization is crucial for scalable applications. Master caching strategies, database optimization, queue management, and advanced monitoring techniques.
2️⃣ Advanced Caching Strategies
Multi-Layer Caching Implementation:
<?php
// app/Services/CacheService.php
namespace App\Services;
use Illuminate\Support\Facades\{Cache, Redis};
use Illuminate\Contracts\Cache\Repository;
class CacheService
{
protected $cache;
protected $redis;
public function __construct(Repository $cache)
{
$this->cache = $cache;
$this->redis = Redis::connection();
}
/**
* Multi-layer caching with fallback
*/
public function remember(string $key, $ttl, callable $callback, array $tags = [])
{
// Layer 1: Memory cache (fastest)
static $memoryCache = [];
if (isset($memoryCache[$key])) {
return $memoryCache[$key];
}
// Layer 2: Application cache (Redis/Memcached)
$value = $this->cache->tags($tags)->remember($key, $ttl, function () use ($callback, &$memoryCache, $key) {
$result = $callback();
$memoryCache[$key] = $result; // Store in memory for this request
return $result;
});
return $value;
}
/**
* Smart cache invalidation
*/
public function invalidate(string $pattern): int
{
$keys = $this->redis->keys($pattern);
if (empty($keys)) {
return 0;
}
return $this->redis->del($keys);
}
/**
* Cache warming for critical data
*/
public function warm(array $keys): void
{
foreach ($keys as $key => $config) {
if (!$this->cache->has($key)) {
$value = call_user_func($config['callback']);
$this->cache->put($key, $value, $config['ttl'] ?? 3600);
}
}
}
/**
* Distributed cache lock
*/
public function lock(string $key, int $timeout = 10): ?Lock
{
return $this->cache->lock($key, $timeout);
}
/**
* Cache statistics
*/
public function getStats(): array
{
$info = $this->redis->info('memory');
return [
'used_memory' => $info['used_memory_human'] ?? 'N/A',
'hit_rate' => $this->getHitRate(),
'key_count' => $this->redis->dbsize(),
'expired_keys' => $info['expired_keys'] ?? 0,
'evicted_keys' => $info['evicted_keys'] ?? 0
];
}
protected function getHitRate(): string
{
$info = $this->redis->info('stats');
$hits = $info['keyspace_hits'] ?? 0;
$misses = $info['keyspace_misses'] ?? 0;
$total = $hits + $misses;
if ($total === 0) {
return '0%';
}
return round(($hits / $total) * 100, 2) . '%';
}
}
// app/Services/PostCacheService.php - Domain-specific caching
namespace App\Services;
class PostCacheService
{
protected $cacheService;
public function __construct(CacheService $cacheService)
{
$this->cacheService = $cacheService;
}
public function getPopularPosts(int $limit = 10)
{
return $this->cacheService->remember(
"posts.popular.{$limit}",
3600, // 1 hour
function () use ($limit) {
return Post::published()
->withCount(['likes', 'comments', 'views'])
->orderByDesc('likes_count')
->limit($limit)
->get();
},
['posts', 'popular']
);
}
public function getPost(int $id)
{
return $this->cacheService->remember(
"posts.{$id}",
7200, // 2 hours
function () use ($id) {
return Post::with(['user', 'category', 'tags'])->find($id);
},
['posts', "post.{$id}"]
);
}
public function getCategoryPosts(int $categoryId, int $page = 1)
{
return $this->cacheService->remember(
"posts.category.{$categoryId}.page.{$page}",
1800, // 30 minutes
function () use ($categoryId, $page) {
return Post::where('category_id', $categoryId)
->published()
->with(['user', 'tags'])
->paginate(15, ['*'], 'page', $page);
},
['posts', "category.{$categoryId}"]
);
}
public function invalidatePost(Post $post): void
{
$tags = [
'posts',
"post.{$post->id}",
"category.{$post->category_id}",
"user.{$post->user_id}.posts"
];
Cache::tags($tags)->flush();
// Also invalidate related caches
if ($post->is_featured) {
Cache::tags(['popular', 'featured'])->flush();
}
}
public function warmCache(): void
{
$this->cacheService->warm([
'posts.popular.10' => [
'callback' => fn() => $this->getPopularPosts(10),
'ttl' => 3600
],
'posts.recent.20' => [
'callback' => fn() => Post::published()->latest()->limit(20)->get(),
'ttl' => 1800
],
'categories.with_counts' => [
'callback' => fn() => Category::withCount('posts')->get(),
'ttl' => 7200
]
]);
}
}
Response Caching Middleware:
<?php
// app/Http/Middleware/CacheResponse.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class CacheResponse
{
public function handle(Request $request, Closure $next, int $ttl = 300)
{
// Only cache GET requests
if ($request->method() !== 'GET') {
return $next($request);
}
// Don't cache authenticated requests by default
if (auth()->check() && !$request->has('public')) {
return $next($request);
}
$cacheKey = $this->getCacheKey($request);
// Try to get cached response
$cachedResponse = Cache::get($cacheKey);
if ($cachedResponse) {
return response($cachedResponse['content'])
->withHeaders($cachedResponse['headers'])
->header('X-Cache-Status', 'HIT');
}
// Generate response
$response = $next($request);
// Only cache successful responses
if ($response->getStatusCode() === 200) {
$cacheData = [
'content' => $response->getContent(),
'headers' => [
'Content-Type' => $response->headers->get('Content-Type'),
'Cache-Control' => 'public, max-age=' . $ttl
]
];
Cache::put($cacheKey, $cacheData, $ttl);
$response->header('X-Cache-Status', 'MISS');
}
return $response;
}
protected function getCacheKey(Request $request): string
{
$url = $request->fullUrl();
$acceptHeader = $request->header('Accept', 'text/html');
return 'response_cache:' . md5($url . $acceptHeader);
}
}
// Usage in routes
Route::middleware(['cache.response:3600'])->group(function () {
Route::get('/blog', [BlogController::class, 'index']);
Route::get('/blog/{post}', [BlogController::class, 'show']);
});
4️⃣ Queue & Job Optimization
High-Performance Queue Configuration:
<?php
// app/Jobs/OptimizedEmailJob.php
namespace App\Jobs;
use Illuminate\Bus\{Queueable, Batchable};
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\{InteractsWithQueue, SerializesModels, Middleware\WithoutOverlapping};
class OptimizedEmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, Batchable;
public $timeout = 120;
public $maxExceptions = 3;
public $backoff = [10, 30, 60]; // Exponential backoff
protected $userId;
protected $emailData;
public function __construct(int $userId, array $emailData)
{
$this->userId = $userId;
$this->emailData = $emailData;
// Optimize queue routing
$this->onQueue('emails');
}
public function middleware(): array
{
return [
// Prevent duplicate jobs
new WithoutOverlapping($this->userId),
// Rate limiting
(new RateLimited('emails'))->allow(100)->everyMinute(),
];
}
public function handle(): void
{
$user = User::find($this->userId);
if (!$user || !$user->email_notifications_enabled) {
return;
}
try {
Mail::to($user)->send(new UserNotification($this->emailData));
// Update user activity
$user->touch('last_email_sent_at');
} catch (Exception $e) {
// Log the error
Log::error('Email job failed', [
'user_id' => $this->userId,
'error' => $e->getMessage(),
'attempts' => $this->attempts()
]);
// Release job for retry if attempts remaining
if ($this->attempts() < $this->maxExceptions) {
$this->release($this->backoff[$this->attempts() - 1] ?? 60);
} else {
$this->fail($e);
}
}
}
public function failed(Exception $exception): void
{
// Handle permanent failure
Log::critical('Email job permanently failed', [
'user_id' => $this->userId,
'exception' => $exception->getMessage()
]);
// Notify administrators
NotificationFacade::route('slack', '#alerts')
->notify(new JobFailedNotification($this, $exception));
}
}
// app/Console/Commands/OptimizeQueues.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\{Queue, Redis};
class OptimizeQueues extends Command
{
protected $signature = 'queue:optimize';
protected $description = 'Optimize queue performance and clean up failed jobs';
public function handle(): void
{
$this->info('Optimizing queue performance...');
// Clean up old failed jobs
$this->cleanupFailedJobs();
// Balance queue workers
$this->balanceWorkers();
// Monitor queue health
$this->monitorQueueHealth();
$this->info('Queue optimization completed!');
}
protected function cleanupFailedJobs(): void
{
$olderThan = now()->subDays(7);
$deleted = DB::table('failed_jobs')
->where('failed_at', '<', $olderThan)
->delete();
$this->line("Cleaned up {$deleted} old failed jobs");
}
protected function balanceWorkers(): void
{
$queueSizes = [
'default' => Queue::size('default'),
'emails' => Queue::size('emails'),
'high' => Queue::size('high'),
'low' => Queue::size('low')
];
$this->table(
['Queue', 'Size', 'Recommended Workers'],
collect($queueSizes)->map(function ($size, $name) {
$workers = $this->calculateOptimalWorkers($size);
return [$name, $size, $workers];
})->values()->toArray()
);
}
protected function calculateOptimalWorkers(int $queueSize): int
{
if ($queueSize === 0) return 1;
if ($queueSize <= 10) return 2;
if ($queueSize <= 100) return 5;
if ($queueSize <= 1000) return 10;
return 20;
}
protected function monitorQueueHealth(): void
{
$redis = Redis::connection();
$info = $redis->info('memory');
$memoryUsage = $info['used_memory_human'] ?? 'Unknown';
$this->line("Redis memory usage: {$memoryUsage}");
// Check for stuck jobs
$stuckJobs = DB::table('jobs')
->where('reserved_at', '<', now()->subMinutes(30))
->whereNotNull('reserved_at')
->count();
if ($stuckJobs > 0) {
$this->warn("Found {$stuckJobs} potentially stuck jobs");
}
}
}
Queue Monitoring Dashboard:
<?php
// app/Http/Controllers/Admin/QueueMonitorController.php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\{Queue, DB, Redis};
class QueueMonitorController extends Controller
{
public function dashboard()
{
$stats = [
'queue_sizes' => $this->getQueueSizes(),
'failed_jobs' => $this->getFailedJobsStats(),
'worker_status' => $this->getWorkerStatus(),
'throughput' => $this->getThroughputStats(),
'memory_usage' => $this->getMemoryUsage()
];
return view('admin.queue-dashboard', compact('stats'));
}
protected function getQueueSizes(): array
{
return [
'default' => Queue::size('default'),
'high' => Queue::size('high'),
'emails' => Queue::size('emails'),
'low' => Queue::size('low')
];
}
protected function getFailedJobsStats(): array
{
return [
'total' => DB::table('failed_jobs')->count(),
'last_24h' => DB::table('failed_jobs')
->where('failed_at', '>=', now()->subDay())
->count(),
'by_queue' => DB::table('failed_jobs')
->select('queue', DB::raw('COUNT(*) as count'))
->groupBy('queue')
->get()
->pluck('count', 'queue')
->toArray()
];
}
protected function getWorkerStatus(): array
{
// This would integrate with your process manager
return [
'active_workers' => 5, // Get from supervisor/systemd
'idle_workers' => 2,
'busy_workers' => 3
];
}
protected function getThroughputStats(): array
{
$hourly = [];
for ($i = 23; $i >= 0; $i--) {
$hour = now()->subHours($i)->format('Y-m-d H:00:00');
$hourly[$hour] = DB::table('job_batches')
->where('finished_at', '>=', $hour)
->where('finished_at', '<', now()->subHours($i-1)->format('Y-m-d H:00:00'))
->sum('total_jobs') ?? 0;
}
return $hourly;
}
protected function getMemoryUsage(): array
{
$redis = Redis::connection();
$info = $redis->info();
return [
'used_memory' => $info['used_memory_human'] ?? 'N/A',
'peak_memory' => $info['used_memory_peak_human'] ?? 'N/A',
'fragmentation_ratio' => $info['mem_fragmentation_ratio'] ?? 'N/A'
];
}
}
Next Steps
Deploy your optimized Laravel application with Deployment & DevOps strategies for production environments.
📩 Need help with Laravel performance?
Article Category
Laravel Performance Optimization: Advanced Techniques & Monitoring
Master Laravel performance optimization with caching, database optimization, queue management, profiling tools, and enterprise-level scaling strategies.
Consultation & Communication
Direct communication via WhatsApp or phone to understand your project needs precisely.
Planning & Scheduling
Creating clear work plan with specific timeline for each project phase.
Development & Coding
Building projects with latest technologies ensuring high performance and security.
Testing & Delivery
Comprehensive testing and thorough review before final project delivery.