Redis Caching: Make Your App 10x Faster
Database queries killing your performance? Redis caching can make your app instant. Here's how to do it right.
Table of Contents
- The Problem
- Basic Caching
- Setup Redis
- Cache Strategies
- 1. Cache Expensive Queries
- 2. Cache Per User
- 3. Cache with Tags
- Invalidating Cache
- Manual Invalidation
- Automatic Invalidation
- Real Project Example
- Cache Patterns
- 1. Cache-Aside
- 2. Write-Through
- 3. Write-Behind
- What to Cache
- Cache Duration
- Monitoring Cache
- Cache Hit Rate
- Cache Size
- Common Mistakes
- Mistake 1: Caching Everything
- Mistake 2: No Expiration
- Mistake 3: Not Invalidating
- Node.js Redis Caching
- Bottom Line
Redis Caching: Make Your App 10x Faster
Your dashboard takes 3 seconds to load. Same data. Every time.
Redis can make it instant.
Here's how.
The Problem
public function dashboard()
{
$stats = [
'total_users' => User::count(),
'total_orders' => Order::count(),
'revenue' => Order::sum('total'),
'pending_orders' => Order::where('status', 'pending')->count(),
];
return view('dashboard', $stats);
}
4 database queries. Every page load. Even though data changes once per hour.
Basic Caching
public function dashboard()
{
$stats = Cache::remember('dashboard_stats', 3600, function () {
return [
'total_users' => User::count(),
'total_orders' => Order::count(),
'revenue' => Order::sum('total'),
'pending_orders' => Order::where('status', 'pending')->count(),
];
});
return view('dashboard', $stats);
}
First request: Runs queries, caches result.
Next 3600 seconds: Returns cached data instantly.
Performance:
- Before: 3.2 seconds
- After: 0.05 seconds
Setup Redis (Laravel)
composer require predis/predis
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
php artisan config:clear
Done.
Cache Strategies
1. Cache Expensive Queries
// Bad: Query every time
$products = Product::with('category', 'images')
->where('featured', true)
->get();
// Good: Cache for 1 hour
$products = Cache::remember('featured_products', 3600, function () {
return Product::with('category', 'images')
->where('featured', true)
->get();
});
2. Cache Per User
$userId = auth()->id();
$orders = Cache::remember("user_{$userId}_orders", 600, function () use ($userId) {
return Order::where('user_id', $userId)
->with('items')
->latest()
->get();
});
Each user has their own cache.
3. Cache with Tags
// Cache with tags
Cache::tags(['products', 'featured'])->put('featured_products', $products, 3600);
// Invalidate all product caches
Cache::tags('products')->flush();
Use case: When product updates, clear all product-related caches.
Invalidating Cache
Manual Invalidation
public function update(Request $request, Product $product)
{
$product->update($request->validated());
// Clear cache
Cache::forget('featured_products');
Cache::forget("product_{$product->id}");
return response()->json($product);
}
Automatic Invalidation (Model Events)
class Product extends Model
{
protected static function booted()
{
static::saved(function ($product) {
Cache::forget('featured_products');
Cache::forget("product_{$product->id}");
});
static::deleted(function ($product) {
Cache::forget('featured_products');
Cache::forget("product_{$product->id}");
});
}
}
Cache clears automatically when product changes.
Real Project Example
Client: News website, homepage loading slow
Before caching:
public function index()
{
$data = [
'latest_news' => News::latest()->take(10)->get(),
'trending' => News::orderBy('views', 'desc')->take(5)->get(),
'categories' => Category::withCount('news')->get(),
];
return view('home', $data);
}
- Load time: 2.8 seconds
- Database queries: 13
- Server load: High
After caching:
public function index()
{
$data = Cache::remember('homepage_data', 300, function () {
return [
'latest_news' => News::latest()->take(10)->get(),
'trending' => News::orderBy('views', 'desc')->take(5)->get(),
'categories' => Category::withCount('news')->get(),
];
});
return view('home', $data);
}
- Load time: 0.2 seconds
- Database queries: 0 (cached)
- Server load: 70% reduction
Cache Patterns
1. Cache-Aside
// Check cache first
$user = Cache::get("user_{$id}");
if (!$user) {
// Not in cache, get from database
$user = User::find($id);
// Store in cache
Cache::put("user_{$id}", $user, 3600);
}
return $user;
2. Write-Through
public function update(Request $request, User $user)
{
$user->update($request->validated());
// Update cache immediately
Cache::put("user_{$user->id}", $user, 3600);
return $user;
}
3. Write-Behind
public function incrementViews(Product $product)
{
// Increment in cache
Cache::increment("product_{$product->id}_views");
// Update database later (queue job)
UpdateProductViews::dispatch($product->id)->delay(now()->addMinutes(5));
}
What to Cache
Always cache:
- Expensive queries (> 100ms)
- Data that rarely changes
- Aggregations (counts, sums)
- API responses from external services
Never cache:
- User-specific sensitive data
- Real-time data
- Data that changes constantly
- Small, fast queries
Cache Duration
// Static content: 1 day
Cache::remember('footer_links', 86400, fn() => Link::all());
// Semi-static: 1 hour
Cache::remember('featured_products', 3600, fn() => Product::featured()->get());
// Dynamic: 5 minutes
Cache::remember('trending_posts', 300, fn() => Post::trending()->get());
// Very dynamic: 30 seconds
Cache::remember('active_users', 30, fn() => User::online()->count());
Monitoring Cache
Cache Hit Rate
// Track cache hits
$cacheHits = Cache::get('cache_hits', 0);
$cacheMisses = Cache::get('cache_misses', 0);
$hitRate = $cacheHits / ($cacheHits + $cacheMisses) * 100;
// Aim for > 80% hit rate
Cache Size
# Redis CLI
redis-cli
> INFO memory
> DBSIZE
Monitor memory usage. Clear old keys if needed.
Common Mistakes
Mistake 1: Caching Everything
// Don't cache fast queries
Cache::remember('user_count', 3600, fn() => User::count());
// If User table is small, query is instant. Caching adds overhead.
Mistake 2: No Expiration
// Bad: Cache forever
Cache::forever('data', $value);
// Good: Always set expiration
Cache::put('data', $value, 3600);
Mistake 3: Not Invalidating
// Update product
$product->update(['price' => 2000]);
// Forgot to clear cache
// Users still see old price
Always invalidate related caches.
Node.js Redis Caching
const redis = require('redis');
const client = redis.createClient();
// Cache data
async function getProducts() {
const cached = await client.get('products');
if (cached) {
return JSON.parse(cached);
}
const products = await db.query('SELECT * FROM products');
await client.setEx('products', 3600, JSON.stringify(products));
return products;
}
Bottom Line
Redis caching is the easiest performance win.
Cache expensive queries. Set reasonable expiration. Invalidate when data changes.
Can make your app 10x faster with minimal code.
Need performance optimization?
We optimize slow applications for Nigerian businesses. Database tuning, caching, scaling.
📞 WhatsApp: +234 708 711 0468
📧 info@raspibtech.com
📍 Lagos Island
Related:
Need Help with Your Project?
Let's discuss how Raspib Technology can help transform your business
Related Articles
Laravel 11: What Changed and Why You Should Care
Laravel 11 is out. Slimmer structure, better performance, and features that actually save time. Here's what matters.
Read more →Laravel 12: The Upgrade You've Been Waiting For
Laravel 12 brings major improvements. Here's what changed and why it matters for your projects.
Read more →Next.js 15: The Features That Actually Matter
Next.js 15 changed a lot. Here's what affects your projects, what breaks, and when to upgrade.
Read more →