There are a lot of different ways that people use memcached and PHP. The most common of which is probably your basic set and get to cache data from your database.
function get_my_data1() {
$cache_id = "mykey";
$data = $memcache_obj->get($cache_id);
if ( !$data ) {
$data = get_data_from_db_function();
$memcache_obj->set($cache_id, $data, $sec_to_cache_for);
}
return $data;
}
But what if the query that’s going to hit the database is pretty intensive and you don’t want more than one user to hit the db at a time? That’s easily handling via a semaphore lock.
function get_my_data2() {
$cache_id = "mykey";
$data = $memcache_obj->get($cache_id);
if ( !$data ) {
// check to see if someone has already set the lock
$data_lock = $memcache_obj->get($cache_id . '_qry_lock');
if ( $data_lock ) {
$lock_counter = 0;
// loop until you find that the lock has been released. that implies that the query has finished
do while ( $data_lock ) {
// you may only want to wait for a specified period of time.
// one second is usually sufficient since your goal is to always have sub-second response time
// if you query takes more than 1 second, you should consider "warming" your cached data via a cron job
if ( $lock_counter > $max_time_to_wait ) {
$lock_failed = true;
break;
}
// you really want this to be a fraction of a second so the user waits as little as possible
// for the simplicity of example, I'm using the sleep function.
sleep(1);
$data_lock = $memcache_obj->get($cache_id . '_qry_lock');
}
// if the loop is completed, that either means the user waited for too long
// or that the lock has been removed. try to get the cached data again; it should exist now
$data = $memcache_obj->get($cache_id);
if ( $data ) {
return $data;
}
}
// set a lock for 2 seconds
$memcache_obj->set($cache_id . '_qry_lock', true, 2);
$data = get_data_from_db_function();
$memcache_obj->set($cache_id, $data, $sec_to_cache_for);
// don't forget to remove the lock
$memcache_obj->delete($cache_id . '_qry_lock');
}
return $data;
}
More below the break –>
Another good use of the semaphore locking is to set the expire time as part of the cached data and have an extended expire time set in memcache. This allows you to have more control over what happens when the cached data becomes stale. You can make it so that one user repopulates the cache while other users continue to get the existing cache until the first user has finished.
function get_my_data3() {
$cache_id = "mykey";
$data = $memcache_obj->get($cache_id);
// if there is cached data and the expire timestamp has already expired or is within the next 2 minutes
// then we want the user to freshen up the cached data
if ( $data && ($data['cache_expires_timestamp'] - time()) < 120 ) {
// if the semaphore lock has already been set, just return the data like you normally would.
if ( $memcache_obj->get($cache_id . '_expire_lock') ) {
return $data;
}
// now we want to set the lock and have the user freshen the data.
$memcache_obj->set($cache_id . '_expire_lock', true, 2);
// by unsetting the data it will cause the data gather logic below to execute.
unset($data);
}
if ( !$data ) {
// be sure to include all of the semaphore logic from example 2
// set the _qry_lock for 2 seconds
$memcache_obj->set($cache_id . '_qry_lock', true, 2);
$raw_data = get_data_from_db_function();
$data['cache_expires_timestamp'] = time() + $sec_to_cache_for;
$data['cached_data'] = $raw_data;
$memcache_obj->set($cache_id, $data, $sec_to_cache_for);
// remove the _qry_lock
$memcache_obj->delete($cache_id . '_qry_lock');
// remove the _expires_lock
$memcache_obj->delete($cache_id . '_expires_lock');
}
return $data;
}
When you mash these functions together, you end up with a system where only one user every freshens the cache and/or hits the database with a specific query at a time. There are a lot of other things that suddenly become available once you start thinking of memcached beyond just saving your db from hits. You have things like session handling, smarty template caching, flags for per-server processing (e.g., clearing local file cache), and even as a temporary database.
Posted under PHP, Tips & Tricks, Web Development, mysql
This post was written by Michael Tougeron on January 11, 2008

Good stuff!!