Cache MySQL query results with PHP WinCache

Learn how to cache and store the MySQL query result in your web server RAM memory, utilizing PHP WinCache or OPcache
Published on Thursday, 14 February 2013

In November 2011, I wrote a post about MySQL query caching with PHP/Zend_Cache, and I recently stumbled upon a blog post caching MySQL query results in memcached by "KutuKupret". This made me wonder if the same would be easily done with the Windows Cache Extension for PHP.

Yes, and it turns out to be pretty easy as well! You can cache and store the MySQL query result in your web server RAM memory, utilizing PHP WinCache. Or another PHP opcode cache like OPcache for that matter. This'll  increase database and PHP performance.

Here is a simple PHP, WinCache and MySQL example.

Well, you almost can't call this an example or demonstration, because I use only one record in the table. But here goes... Don't forget to use SQL_NO_CACHE to disable MySQL's own caching mechanism.

Setup a simple table in your MySQL database

CREATE TABLE winc (
 personID int NOT NULL AUTO_INCREMENT,
 PRIMARY KEY( personID ),
 FirstName varchar( 15 ),
 LastName varchar( 15 ),
 Age int
);
INSERT INTO winc ( FirstName, LastName, Age )
VALUES ( 'Memory', 'Cache', '100' );

PHP script to cache MySQL query results in WinCache's memory

Now create a simple PHP script to access the MySQL database, run the query, store the query in WinCache's ucache, and return the result. PHP code taken from KutuKupret his earlier mentioned blogpost and modified for this case.

All we have to do is generate a unique key for our query result and store that key (with its result of course) in the WinCache memory. We use an md5-hash for generating a unique key, wincache_ucache_add for adding the key/value to the WinCache memory and wincache_ucache_get to get a result if the key is found.

<?php
/**
 * WinCache Extension for PHP
 * store MySQL query result in Wincache user cache (ucache)
 * http://www.php.net/manual/en/function.wincache-ucache-add.php
 * 
 * 14-02-2013 - jan@saotn.nl, www.saotn.org Sysadmins of the North
 * Twitter: @Jan_Reilink
 * Donate: https://www.paypal.me/jreilink
 */

$dbhost = 'mysql.example.com';
$dbuser = 'examplecom';
$dbpass = 'pass_word';
$conn = mysql_connect( $dbhost, $dbuser, $dbpass )
  or die ( 'Error connecting to mysql' );
 
$dbname = 'examplecom';
mysql_select_db( $dbname );
 
$key = md5( "SELECT * FROM winc where FirstName='Memory'" );
$get_result = array();
$get_result = wincache_ucache_get( $key );
 
if ( $get_result ) {
  echo "FirstName: " . $get_result['FirstName'] . "\n";
  echo "LastName: " . $get_result['LastName'] . "\n";
  echo "Age: " . $get_result['Age'] . "\n";
  echo "Retrieved From Cache\n";
} else {
  // Run the query and get the data from the database then cache it
  // Disable MySQL Query Cache with SQL_NO_CACHE for testing!
  $query = "SELECT * FROM winc where FirstName='Memory';";
  $result = mysql_query( $query );
 
  $row = mysql_fetch_array( $result );
  echo "FirstName: " . $row[1] . "\n";
  echo "LastName: " . $row[2] . "\n";
  echo "Age: " . $row[3] . "\n";
  echo "Retrieved from the Database\n";

  // Store the result of the query for 30 seconds
  wincache_ucache_add( $key, $row, 30 );
 
  mysql_free_result( $result );
}

// Did you like this code or article? Then please donation to
// https://paypal.me/jreilink. It'll be used for coffee, thanks! :)
?>

First time access, the query will be access directly from the MySQL database and displayed to the browser.

FirstName: Memory
LastName: Cache
Age: 100
Retrieved from the Database

Reload your browser, and now the query is pulled from WinCache's memory and displayed to the browser.

FirstName: Memory
LastName: Cache
Age: 100
Retrieved From Cache

Timing test result

time wget -q -O - dev.example.org/dev/mysqlc.php
FirstName: Memory
LastName: Cache
Age: 100
Retrieved from the Database

real    0m0.222s
user    0m0.000s
sys     0m0.000s
time wget -q -O - dev.example.org/dev/mysqlc.php
FirstName: Memory
LastName: Cache
Age: 100
Retrieved From Cache

real    0m0.035s
user    0m0.000s
sys     0m0.000s

By storing MySQL query result in memory, we get a nice performance gain! (:

PHP code optimization: only create a MySQL database connection when no cache entry is found

(updated 2013-10-13)

As someone correctly mentioned on stackoverflow, to further optimize the PHP code, it's better to only make a database connection when no cache entry is found.

We do so by slightly altering the code and moving mysql_connect and mysql_select_db down:

<?php
$dbhost = 'mysql.example.com';
$dbuser = 'examplecom';
$dbpass = 'pass_word';
$dbname = 'examplecom';

$key = md5( "SELECT * FROM winc where FirstName='Memory'" );
$get_result = array();
$get_result = wincache_ucache_get( $key );
 
if ( $get_result ) {
  echo "FirstName: " . $get_result['FirstName'] . "\n";
  echo "LastName: " . $get_result['LastName'] . "\n";
  echo "Age: " . $get_result['Age'] . "\n";
  echo "Retrieved From Cache\n";
} else {
  // Make a database connection and run the query and get the data from 
  // the database. Then cache it
  // Disable MySQL Query Cache with SQL_NO_CACHE for testing!

  $conn = mysql_connect( $dbhost, $dbuser, $dbpass )
          or die ( 'Error connecting to mysql' );
  mysql_select_db( $dbname );

  $query = "SELECT * FROM winc where FirstName='Memory';";
  $result = mysql_query( $query );
 
  $row = mysql_fetch_array($result);
  echo "FirstName: " . $row[1] . "\n";
  echo "LastName: " . $row[2] . "\n";
  echo "Age: " . $row[3] . "\n";
  echo "Retrieved from the Database\n";

  // Store the result of the query for 30 seconds
  wincache_ucache_add( $key, $row, 30 );
  mysql_free_result( $result );
}

// Did you like this code or article? Then please donation to
// https://paypal.me/jreilink. It'll be used for coffee, thanks! :)
?>

PHP ext/mysql vs MySQLi functions

This article really needs an update, since it still uses the PHP ext/mysql functions to communicate with your MySQL database. These functions are deprecated.

If you want to use these examples to store MySQL query results in WinCache's memory, update the examples to MySQLi or PDO!