O'Reilly

Improving Web App Performance With Memcached

By on  

When we think of storage in a web application our first thought is usually a traditional database like MySQL. This is great for long term storage and data analysis, but there's a better option for many short-term needs: memcached. It's a great choice for saving bits of information between page requests and increasing performance. In this introduction we'll show you how to start using memcached with PHP.

Introduction

Memcached is simply a server with an interface to let you store things in memory. This can run on the same machine as your web server, but scalability comes from distributing instances across multiple servers. All you need is the memcached daemon running and PHP provides a simple interface with a PECL library. On a Debian-based Linux system this is as easy as:

$ sudo apt-get install memcached
$ sudo service memcached start
$ sudo pecl install memcached
$ sudo service httpd restart

Now we should mention there are technically two PHP libraries available to work with memcached. The older library is called "memcache" and is lacking in some features. The newer "memcached" library uses libmemcached and is generally preferred.

The first step in PHP is to connect to the server. Connections can be persisted across requests, which is nice for performance. Then add to the server list as needed. In this case we'll use a locally running instance on the default port:

function get_memcached() {
    // Set a persistent connection ID
    $mc = new Memcached('webapp');
    // Set a short timeout (in milliseconds) so if the server goes down
    // it doesn't take down our site with it
    $mc->setOption(Memcached::OPT_CONNECT_TIMEOUT, 1000);
    if ( !$mc->getServerList() ) {
        if ( !$mc->addServer('localhost', 11211) ) {
            error_log('Could not add memcached server.');
            return false;
        }
    }
    return $mc;
}

Now you can read and write PHP variables to memcached with simple functions based on keys you define. They'll be serialized and deserialized automatically. You can't write resources like database connections or result sets, but you can turn those result sets into arrays and store those.

$mc->set('list', array(1, 2, 3));
$list = $mc->get('list');

Storing Data

Let's say we want to store a list of recently visited URLs for each logged in user. We could use sessions but that wouldn't work across devices and it'll disappear as soon as the session is cleared. We could use a database but that's slow for this kind of data that's most likely not critical to our system. Using memcached this is easy:

$user_id = 123;  // The current user's ID

$recents = array();
if ($mc = get_memcached()) {
    // Get any stored in memcached
    $recents = $mc->get("recents-$user_id");
    if (!$recents) {
        $recents = array();
    }
    $recents[] = $_SERVER['REQUEST_URI'];
    $recents = array_unique($recents);
    $mc->set("recents-$user_id", $recents);
}

print_r($recents);

Caching

Now let's really boost your web application by caching database results. Database queries are usually the biggest bottleneck in server processing, so avoiding duplicate queries by caching results in memory can provide a huge performance gain. The simplest method is to query and store just with primary keys. Typically it's best that the cached value be deleted when the database record is updated so a user never sees any out-of-date values.

function store_product($id, $name, $price) {
    // Create or update the product in the database
    $db = get_db();
    $qry = $db->prepare('REPLACE INTO product (id, name, price) VALUES (:id, :name, :price)');
    $qry->bindParam(':id', $id);
    $qry->bindParam(':name', $name);
    $qry->bindParam(':price', $price);
    $qry->execute();

    // Remove outdated values from cache if it's there
    if ($mc = get_memcached()) {
        $mc->delete("product-$id");
    }
}

function get_product($id) {
    $product = null;

    // First check the cache
    if ($mc = get_memcached()) {
        $product = $mc->get("product-$id");
    }

    if (!$product) {
        // Not in the cache; check the db
        $qry = $db->prepare('SELECT * FROM product WHERE id = $id');
        $qry->bindParam(':id', $id);
        if ($qry->execute()) {
            $product = $qry->fetch();

            // Cache for future use
            if ($mc) {
                $mc->set("product-$id", $product);
            }
        }
    }

    return $product;
}

Caveats

As with any technology choice there are limitations and things to watch out for:

  • The max length of a key is 250 bytes. Keep your keys simple and short.
  • The default max value size is about 1MB. This isn't the right place to store large values.
  • Storage isn't locked for reading or writing in the way database records can be locked. Just be aware that any web request can update any value at any time.
  • Be sure your memcached servers have enough combined RAM.

Next Steps

There's more you can do with memcached:

  • Cached values can have timeouts. This is useful when data should be cached for a set period of time instead of deleted manually.
  • Simple increment and decrement methods are useful for keeping quick counters between requests.
  • With memcached configured correctly you can share data between applications written in many programming languages.

Give memcached a try. In the right scenarios it's a very simple and effective solution to maximize your web app performance.

Matthew Schwartz

About Matthew Schwartz

Matthew is a software architect and lead developer at Reflexions Data in NYC. In his spare time he runs DocForge.com, cooks, and shoots a mean game of pocket billiards.

Track.js Error Reporting

Recent Features

  • 9 Mind-Blowing WebGL Demos

    As much as developers now loathe Flash, we're still playing a bit of catch up to natively duplicate the animation capabilities that Adobe's old technology provided us.  Of course we have canvas, an awesome technology, one which I highlighted 9 mind-blowing demos.  Another technology available...

  • 5 Awesome New Mozilla Technologies You’ve Never Heard Of

    My trip to Mozilla Summit 2013 was incredible.  I've spent so much time focusing on my project that I had lost sight of all of the great work Mozillians were putting out.  MozSummit provided the perfect reminder of how brilliant my colleagues are and how much...

Incredible Demos

  • MooTools: Set Style Per Media

    I'd bet one of the most used MooTools methods is the setStyle() method, which allows you to set CSS style declarations for an element. One of the limitations of MooTools' setStyle() method is that it sets the specific style for all medias. What if...

  • Generate Dojo GFX Drawings from SVG Files

    One of the most awesome parts of the Dojo / Dijit / DojoX family is the amazing GFX library.  GFX lives within the dojox.gfx namespace and provides the foundation of Dojo's charting, drawing, and sketch libraries.  GFX allows you to create vector graphics (SVG, VML,...

Discussion

  1. Memcache is fantastic, especially if you are using AWS. We used it to write a page-caching layer across multiple servers in PHP and it worked a treat. Great post.

    • AWS’s ElastiCache is memcached compatible so it’s a good choice for a very easy scalable setup. Very little setup required.

  2. The PECL memcached extension also provides direct support for session storage, so you can avoid the problem of sticky hosts.

    http://php.net/manual/en/memcached.sessions.php

  3. Fabien Ménager

    Redis is also a good alternative if you want to deal with concurrent requests, as every command is atomic, and you can also execute a set of commands atomically with MULTI / EXEC.

    And about session storage in Memcached, in my experience it’s not a good idea as session can be removed at any time if the cache is full for example, which is not really what a user expects.

  4. Markus

    another thing to consider. If you don’t need the distributed nature of memcache/memcached and you like to store/read small chunks of data, you should consider also APC (or APCu since PHP5.5) which is even faster in terms of IO

  5. ic3gh05t

    Quick note. The ‘$id’ in the prepare statement should be ‘:id’

    $qry = $db->prepare(‘SELECT * FROM product WHERE id = $id’);
    $qry->bindParam(‘:id’, $id);

    But this is just a minor bug. What i’m really wondering is if binding a parameter to a non-defined placeholder in the prepare statement will issue a fatal error and prevent query execution. Because if not, the above statement is still vulnerable to a sql injection.

    Thanks for the quick intro to memcached btw, never used it so far but the idea sounds great

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • OSCON Portland:  Conference  Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...

  • Follow Redirects with cURL

    I love playing around with cURL. There's something about loading websites via command line that makes me feel like some type of smug hacker, just like tweeting from command line does. I recently cURL'd the Google homepage and saw the following: I found it weird that Google...

  • Developers Have WordPress, Amateurs Have Squarespace, Professional Designers Have the NEW Webydo!

    Web design platforms have traditionally come in one of two varieties. There are the solutions like WordPress and Drupal that are incredibly powerful, but an understanding of web development and coding is required to be able to use those platforms effectively. On the other side of the...

  • Chris Coyierâs Favorite CodePen Demos II

    Hey everyone! Before we get started, I just want to say it’s damn hard to pick this few favorites on CodePen. Not because, as a co-founder of CodePen, I feel like a dad picking which kid he likes best (RUDE). But because there is just so...

  • GSAP + SVG For Power Users: Motion Along A Path

    Now that the GreenSock API is picking up steam, there are many tutorials and Getting Started guides out there to provide good introductions to the library, not to mention GreenSock’s own Forum and Documentation. This article isn’t intended for beginners, but rather a...