Skip to Content
Course content

203: Caching with Memcached

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

Imagine you're a chef in a busy kitchen. In the back of the building, there's a massive walk-in freezer—that's your database. It has everything you need, but it's a long walk, the door is heavy, and it takes time to find exactly what you're looking for. If you have to walk to that freezer every single time a customer orders a salad just to get one leaf of lettuce, your service is going to be painfully slow.

To fix this, you use a "prep station" right next to your cutting board. You grab a big bowl of pre-washed lettuce from the freezer once, and you keep it right there in front of you. Now, when the next ten orders come in, you aren't walking; you're just reaching. That prep station is exactly what Memcached is for your PHP application.

In technical terms, the freezer is your disk-based database (like MySQL), and the prep station is Memcached—a high-performance, distributed memory object caching system. It stores data in RAM, which is orders of magnitude faster than reading from a disk.

Moving from the Pantry to the Prep Station

When we use Memcached, we don't just dump everything into it. We use a pattern I call "Check-Fetch-Store." You don't just ask the database for data; you ask the cache first. If it's there (a "cache hit"), you're done. If it's not (a "cache miss"), you go to the database, get the data, and then save a copy in the cache for the next person.

Let's look at a real example. Suppose you have a function that fetches a complex list of "Featured Products" from your database. This query involves three joins and takes about 500ms to run. That's a lifetime in web performance.

<?php
$m = new Memcached();
// We connect to the local Memcached server
$m->addServer('localhost', 11211);

$cacheKey = 'featured_products_list';

// 1. Check: Is the data already in our "prep station"?
$products = $m->get($cacheKey);

if ($products === false) {
    // 2. Fetch: Cache miss! We have to walk to the "freezer" (Database)
    echo "Fetching from database... "; 
    
    // Simulating a heavy database query
    $products = $db->query("SELECT * FROM products WHERE featured = 1 LIMIT 10")->fetchAll();
    
    // 3. Store: Put it in the cache so we don't have to do this again
    // We'll set it to expire in 3600 seconds (1 hour)
    $m->set($cacheKey, $products, 3600);
} else {
    echo "Fetching from cache! ";
}

foreach ($products as $product) {
    echo $product['name'] . "
"; } ?>

The Danger of Stale Data

Here is where most developers trip up: the "stale data" problem. If you cache your featured products for an hour, but you change a product's price in the database five minutes later, your customers will still see the old price for another 55 minutes. It's frustrating and can lead to support tickets.

You have two main ways to handle this. First, you can set a shorter TTL (Time To Live)—the third argument in the set() method. If the data changes frequently, maybe cache it for 300 seconds instead of 3600.

The second, more professional approach is "Cache Invalidation." Whenever you update a product in your admin panel, you should explicitly tell Memcached to delete that specific key. I usually do this by calling $m->delete($cacheKey) inside the update logic. This forces the next visitor to trigger a "cache miss," fetching the fresh data from the database and re-caching it. It's the equivalent of the chef throwing away the wilted lettuce and grabbing a fresh bowl from the freezer.

When NOT to use Memcached

I've seen people try to cache everything, and it usually ends in a mess. Don't use Memcached for data that is unique to every single user (like a personal shopping cart) unless you have a very specific reason and a lot of RAM. The real power of Memcached comes from caching "expensive" data that is shared across many users—like site settings, top-level categories, or global API responses.




📋 Practical Task

Exercise: Optimizing a Heavy Category Page

You are working on an e-commerce site where the "Category Tree" (a nested list of all product categories) is generated by a recursive database query. This query is incredibly slow and runs on every single page load because the menu appears on every page.

Your Task:

  • Create a PHP script that implements the "Check-Fetch-Store" pattern using the Memcached class.
  • Use the cache key 'site_category_tree'.
  • Simulate the "heavy database query" by using usleep(500000); (this pauses the script for half a second) and returning a hard-coded array of categories.
  • Set the cache expiration to 15 minutes.
  • Add a simple toggle in your code (e.g., a GET parameter like ?refresh=1) that, when present, calls delete() on the cache key to simulate an admin updating the categories.

Goal: When you refresh the page the first time, you should see a delay. Every subsequent refresh should be nearly instant. When you add ?refresh=1 to the URL, the next load should be slow again as the cache is rebuilt.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.