Docs / Performance Optimization / How to Implement Redis Caching for Web Applications

How to Implement Redis Caching for Web Applications

By Admin · Mar 2, 2026 · Updated Apr 24, 2026 · 26 views · 3 min read

How to Implement Redis Caching for Web Applications

Redis is an in-memory data structure store that provides sub-millisecond response times, making it the most popular caching layer for web applications. Deploying Redis on your Breeze server can dramatically reduce database load and improve response times for frequently accessed data.

Installing Redis

sudo apt update
sudo apt install -y redis-server

Configure Redis for production use:

sudo nano /etc/redis/redis.conf
# Bind to localhost only (security)
bind 127.0.0.1 -::1

# Set a strong password
requirepass YourStrongRedisPassword

# Set max memory and eviction policy
maxmemory 512mb
maxmemory-policy allkeys-lru

# Enable persistence (optional - disable for pure cache)
save 900 1
save 300 10
save 60 10000

# Disable dangerous commands
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
sudo systemctl restart redis-server
sudo systemctl enable redis-server

Installing the PHP Redis Extension

sudo apt install -y php-redis
sudo systemctl restart php8.2-fpm

Verify the extension is loaded:

php -m | grep redis

Basic Caching Patterns

The most common pattern is cache-aside (lazy loading):

$redis = new Redis();
$redis->connect('127.0.0.1');
$redis->auth('YourStrongRedisPassword');

function getCached(Redis $redis, PDO $db, string $key, callable $fetcher, int $ttl = 300) {
    $cached = $redis->get($key);
    if ($cached !== false) {
        return json_decode($cached, true);
    }

    $data = $fetcher($db);
    $redis->setex($key, $ttl, json_encode($data));
    return $data;
}

// Usage
$products = getCached($redis, $db, 'products:featured', function($db) {
    return $db->query("SELECT * FROM products WHERE featured = 1 ORDER BY name")->fetchAll();
}, 600); // Cache for 10 minutes

Cache Invalidation Strategies

The hardest part of caching is knowing when to invalidate:

// 1. Time-based expiration (simplest)
$redis->setex('key', 300, $value); // Expires in 5 minutes

// 2. Explicit invalidation on write
function updateProduct($db, $redis, $id, $data) {
    $db->update('products', $data, ['id' => $id]);

    // Delete specific cache entries
    $redis->del("product:{$id}");
    $redis->del("products:featured");
    $redis->del("products:category:{$data['category_id']}");
}

// 3. Tag-based invalidation using sets
function cacheWithTags(Redis $redis, string $key, $value, array $tags, int $ttl) {
    $redis->setex($key, $ttl, json_encode($value));
    foreach ($tags as $tag) {
        $redis->sAdd("tag:{$tag}", $key);
    }
}

function invalidateTag(Redis $redis, string $tag) {
    $keys = $redis->sMembers("tag:{$tag}");
    if (!empty($keys)) {
        $redis->del(...$keys);
    }
    $redis->del("tag:{$tag}");
}

// Cache a product list with tags
cacheWithTags($redis, 'products:page:1', $products, ['products', 'catalog'], 600);

// When any product changes, invalidate all product caches
invalidateTag($redis, 'products');

Session Storage with Redis

Store PHP sessions in Redis for faster access and easy scaling:

; In php.ini or pool config
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=YourStrongRedisPassword"

Rate Limiting with Redis

Implement sliding window rate limiting:

function checkRateLimit(Redis $redis, string $identifier, int $maxRequests, int $windowSeconds): bool {
    $key = "ratelimit:{$identifier}";
    $now = microtime(true);
    $windowStart = $now - $windowSeconds;

    $pipe = $redis->multi(Redis::PIPELINE);
    $pipe->zRemRangeByScore($key, '-inf', $windowStart);
    $pipe->zCard($key);
    $pipe->zAdd($key, $now, $now . ':' . mt_rand());
    $pipe->expire($key, $windowSeconds);
    $results = $pipe->exec();

    $currentCount = $results[1];
    return $currentCount < $maxRequests;
}

Monitoring Redis Performance

# Connect to Redis CLI
redis-cli -a YourStrongRedisPassword

# Check memory usage
INFO memory

# View hit/miss ratio
INFO stats
# Look for keyspace_hits and keyspace_misses

# Monitor commands in real time
MONITOR

# Check slowlog
SLOWLOG GET 10

# View all keys matching a pattern (use sparingly)
SCAN 0 MATCH "products:*" COUNT 100

Best Practices

  • Set TTLs on all keys — prevent unbounded memory growth
  • Use meaningful key naming — follow a convention like entity:id:attribute
  • Monitor hit rates — a cache with a low hit rate is wasting memory; tune TTLs and warming strategies
  • Use pipelining — batch multiple Redis commands to reduce round trips
  • Serialize efficiently — use json_encode for interoperability or igbinary for smaller payloads
  • Plan for cache failure — your application should degrade gracefully if Redis is unavailable

Was this article helpful?