Skip to Content
Course content

202: Caching with Redis in PHP

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was helping a colleague who had built a beautiful e-commerce storefront. Everything was great until they hit a flash sale. Suddenly, the site ground to a halt. When we looked at the logs, the database was screaming—specifically, one query that generated the main navigation category tree. It was a complex recursive query that ran on every single page load. Even though the categories only changed once a month, the server was recalculating that same tree ten thousand times a minute. He tried indexing the tables, but you can only optimize a query so much before you're fighting the laws of physics.

That's where Redis comes in. Instead of asking the database for the same answer over and over, we just store the final result in memory. Redis is an in-memory data structure store, which means it's orders of magnitude faster than a traditional disk-based database because it doesn't have to deal with the overhead of a relational engine or disk I/O.

Implementing the Cache-Aside Pattern

The most common way you'll use Redis in PHP is through the "Cache-Aside" pattern. The logic is simple: your application first checks the cache. If the data is there (a "cache hit"), you return it immediately. If it's not (a "cache miss"), you fetch it from the database, store it in Redis for next time, and then return it. I've seen developers make the mistake of trying to keep the cache and DB perfectly synced in real-time, but for most use cases, a simple "check, fetch, store" flow is all you need.

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$cacheKey = 'product_categories_tree';
$categories = $redis->get($cacheKey);

if ($categories === false) {
    // Cache miss: This is the expensive part we want to avoid
    $categories = $db->query("SELECT * FROM categories WHERE active = 1")->fetchAll();
    
    // We store the result as a JSON string because Redis stores strings/blobs
    $redis->set($cacheKey, json_encode($categories));
    
    echo "Fetched from Database";
} else {
    $categories = json_decode($categories, true);
    echo "Fetched from Redis";
}
?>

Managing Data Freshness and TTLs

The biggest danger with caching is "stale data." If you cache your category tree forever, and then you add a new category in the admin panel, your customers won't see it until you manually clear the cache. I usually advise against "infinite" caches. Instead, use a Time-To-Live (TTL). By adding an expiration time, you ensure that the data eventually refreshes itself even if you forget to trigger a manual update.

In the example above, I used set(), but you should almost always use setex() (Set with Expiration). It allows you to define the lifetime of the key in seconds in a single atomic operation.

// Cache the data for 3600 seconds (1 hour)
$redis->setex($cacheKey, 3600, json_encode($categories));

If you have a specific event—like a product price update—you can also explicitly delete the cache key using $redis->del($cacheKey). This forces the next visitor to trigger a cache miss and fetch the fresh data from the database.

Handling Complex Data with Redis Hashes

Storing everything as a JSON string is fine for simple lists, but if you're caching individual user profiles or product details, JSON becomes inefficient. If you only need to update a user's "last_login" timestamp, you shouldn't have to decode a giant JSON blob, change one value, and re-encode the whole thing. This is where Redis Hashes come in.

Think of a Hash as a mini-dictionary inside a Redis key. You can set and get individual fields without touching the rest of the object. It keeps your memory usage tighter and your code cleaner when dealing with objects.

// Storing a user session as a hash
$redis->hMSet('user:123', [
    'username' => 'dev_pro',
    'email' => 'dev@example.com',
    'role' => 'admin'
]);

// Fetching only the email
$email = $redis->hGet('user:123', 'email');



📋 Practical Task

Exercise: Implementing a Cached Weather API Wrapper

You are building a weather dashboard that fetches data from a third-party API. The API has a strict rate limit, and the weather doesn't change every second, so fetching it on every page load is both wasteful and risky.

Your Task: Create a PHP script that implements the following logic:

  • Define a cache key based on a city name (e.g., weather:london).
  • Check Redis to see if the weather data for that city already exists.
  • If it exists, return the cached data.
  • If it does not exist, simulate an API call (you can use a sleep(2) to simulate network latency and a hardcoded array of weather data).
  • Store the result in Redis with a TTL of 15 minutes (900 seconds).
  • Print a message to the screen indicating whether the data came from the "API" or the "Cache".
Rating
0 0

There are no comments for now.

to be the first to leave a comment.