HomePHP Page 3 - Caching Result Sets in PHP: Cost-efficient PHP acceleration
A fully functional result set caching system: modifying the original script - PHP
With many websites using a database backend for storing and delivering content, certain common problems arise. One of these is a situation that causes the server to slow down or even bring the system to a complete halt. What can you do to reduce the load on your server? This tutorial discusses one method, a result set caching system, and demonstrates how to implement it with either a procedural or an object-oriented approach.
In order to implement a functional result set caching system, we need to make some changes to the original version of the script, so we can take advantage of the data stored in the cache file, directly reading the result set from that file. Also, it's important to clarify that the script will use a time triggered caching approach, thus forcing a new cache generation each time an expiry timestamp is reached.
Therefore, having defined the overall features of the caching system, the brand new version of the script looks like this:
function writeCache($data,$cacheFile='default_cache.txt') if(!$fp=fopen($cacheFile,'w')){ trigger_error('Error opening cache file'); exit(); } if(!flock($fp,LOCK_EX)){ trigger_error('Unable to lock file'); exit(); } if(!fwrite($fp,serialize($data))){ trigger_error('Error writing to cache file'); exit(); } flock($fp,LOCK_UN); fclose($fp); } function readCache($cacheFile){ if(!file_exists($cacheFile)){ trigger_error('Invalid cache file'); exit(); } return unserialize(file_get_contents($cacheFile)); } function connectMySQL($host,$user,$password,$database){ if(!$db=mysql_connect($host,$user,$password)){ trigger_error('Error connecting to the server '.mysql_error()); exit(); } if(!mysql_select_db($database,$db)){ trigger_error('Error selecting database '.mysql_error()); exit(); } } function query($query){ if(!$result=mysql_query($query)){ trigger_error('Error performing query '.$query.mysql_error()); exit(); } return $result;
} // define cache file $cacheFile='cacheFile.txt'; // define expire time in seconds (2 hours) $expireTime=7200; // check to see if cache file is valid (time triggered caching) if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$expireTime)){ // read data from cache file $data=readCache($cacheFile); } else{ // read data from MySQL connectMySQL('host','user','password','databasename'); $result=query('SELECT * FROM users'); // store result set in array while($row=mysql_fetch_array($result,MYSQL_ASSOC)){ $data[]=$row; } // store serialized data in cache file writeCache($data,$cacheFile); } // display data foreach($data as $key=>$row){ echo 'First Name :'.$row['firstname'].' Last Name :'.$row['lastname'].'<br />'; }
Okay, let's break down the code to analyze in detail the tasks accomplished by each section. First, we've defined some wrapping functions that take care of reading and writing data to the cache file, along with other simple functions that connect to MySQL, select a database and execute a query.
But let's take a look at the "writeCache()" function. As you can see, it accepts two parameters: the data to be written to the cache file, and the name of the cache file being used. This function simply writes serialized data to the given cache file, so in this case the serialized data will be the array obtained from a result set. The referenced function is listed below:
function writeCache($data,$cacheFile='default_cache.txt'){ if(!$fp=fopen($cacheFile,'w')){ trigger_error('Error opening cache file'); exit(); } if(!flock($fp,LOCK_EX)){ trigger_error('Unable to lock file'); exit(); } if(!fwrite($fp,serialize($data))){ trigger_error('Error writing to cache file'); exit(); } flock($fp,LOCK_UN); fclose($fp); }
Notice that I'm using a generic file locking method when writing data to the cache file. However, this might not work properly on some network operating systems. So, be aware of this, in order to implement a file locking method that works in the operating system of your choice.
Now, let's have a look at the "readCache()" function, which reads from the cache file, by reversing the serialized data, and restoring it to its original state:
The only thing to note here is how the data is returned by the function, as stated previously, by using the "unserialize()" function, which is listed below:
By now, we've defined the most relevant functions for implementing the result set caching system. As you might guess, the rest of the functions are fairly self-explanatory. They connect to MySQL, then select a database and finally perform a given query. Here's the list of each one of them, beginning with the "connectMySQL()" function:
function connectMySQL($host,$user,$password,$database){ if(!$db=mysql_connect($host,$user,$password)){ trigger_error('Error connecting to the server '.mysql_error()); exit(); } if(!mysql_select_db($database,$db)){ trigger_error('Error selecting database '.mysql_error()); exit(); } }
I'm sure that you've used similar functions hundreds of times, so I won't spend time on them. Instead, I'll focus primarily on the logic of the caching script. Thus, join me in the next explanation to see how this caching system can be put quickly into action. Let's go!