Dynamic web pages that make constant database calls and run many data processing tasks eat up server resources and increase page load times. In this tutorial, learn 3 content caching techniques to increase web page speed.
More and more sites contain dynamic vs static content web pages. These pages make constant database calls and run many data processing tasks. This eats up server resources and also increases your page load times. In this tutorial I will show you 3 server side content caching methods I’ve used in my web applications.
I will also show you how to use and implement Zend_Cache (part of the Zend Framework) to start caching content in your web apps.
#1 Caching All the Output
Caching page output is a great quick way to increase web page speed. The script content is generated once and cached for a duration of time, subsequent requests always get pulled from the content cache. The key benefit here is that all content going to the browser is cached and almost all server side processing is eliminated.
Using Zend_Cache to implement:
<?php require_once 'Zend/Cache.php'; $cache = Zend_Cache::factory ( 'Page' // frontend caching method , 'File' , array('lifetime'=>3600) , array('cache_dir'=>$_SERVER['DOCUMENT_ROOT'] . '/cache') ); $cache->start('uniquePageName'); // PAGE CONTENT ?>
Keep in mind that this is still a server side solution. There are added benefits to also implementing a client side caching solution using Expire and Modified-If or E-Tag headers where the browser pulls content from it’s web page cache rather than make another server request.
For the moment, it’s not implemented but we plan to add a HTTP conditional system to save bandwidth (the system will send a HTTP 304 Not Modified if the cache is hit and if the browser has already the good version).
#2 Cache Protions of the Output
This technique is rather interesting, allowing portions of a web page to remain dynamic while making other portions static through content caching. The benefit (which is speed) comes when you strike a balance between what content can be static and what remains dynamic. Ask yourself, what does “dynamic” mean to you and your users. If your content does not need to be “to-the-second” fresh, perhaps setting a lower cache expiration time (10-30 minutes) would suffice.
Using Zend_Cache to implement:
<?php require_once 'Zend/Cache.php'; $cache = Zend_Cache::factory ( 'Output' // frontend caching method , 'File' , array('lifetime'=>3600) , array('cache_dir'=>$_SERVER['DOCUMENT_ROOT'] . '/cache') ); if (!($cache->start('uniqueContentName'))) { // PAGE CONTENT $cache->end(); // output buffering ends } // DYNAMIC PAGE CONTENT ?>
#3 Caching Function Output
Another unique concept is the idea of caching the output of functions and methods. Reading cached function output is typically faster then hitting the database with a query. For data that doesn’t often change or data that doesn’t require constant freshness, this may be an ideal solution. I think this caching method is more attractive when dealing with complex web applications where you have interconnected components.
Using Zend_Cache to implement:
<?php function fooBar() { require_once 'Zend/Cache.php'; $cache = Zend_Cache::factory ( 'Core' // frontend caching method , 'File' , array('lifetime'=>3600,'automatic_serialization' => TRUE) , array('cache_dir'=>$_SERVER['DOCUMENT_ROOT'] . '/cache') ); $cache_id = 'uniqueFunctionName'; $rows = $cache->load($cache_id); if (!$rows) { $rows = array(); // FUNCTION CONTENT $cache->save($rows, $cache_id); } return $rows; } ?>
Here is a real world example from a method call I use at YomoMedia:
static function getSubscriberCounts() { require_once 'Zend/Cache.php'; $cache = Zend_Cache::factory ( 'Core' , 'File' , array ( 'lifetime' => 3600 ,'automatic_serialization' => TRUE ) , array ( 'cache_dir' => SP_CACHE_LOCATION ) ); $cache_id = 'Bundle_getSubscriberCounts'; $rows = $cache->load($cache_id); // check if rows were cached if (!$rows) { $rows = array(); $sql = " SELECT b.bundle_id , COUNT(DISTINCT bs.user_id) AS subscriber_count FROM bundle_subscriptions AS bs INNER JOIN bundles AS b ON b.bundle_id = bs.bundle_id INNER JOIN users AS u ON u.user_id = bs.user_id AND u.role != 'temp' GROUP BY b.bundle_id "; $result = @mysql_query($sql); if ($result) { while($row = @mysql_fetch_assoc($result)) { // use id as key to be able to do faster lookups $rows[$row['bundle_id']] = $row['subscriber_count']; } @mysql_free_result($result); // generate a table to provide other methods and queries a JOIN $tb = 'gen_bundle_subscriber_counts'; @mysql_query("DROP TABLE IF EXISTS `$tb`"); $sql_create = " CREATE TABLE `$tb` ( `bundle_id` int(11) unsigned NOT NULL DEFAULT '0' , `subscriber_count` int(10) NOT NULL DEFAULT '0' , PRIMARY KEY (`bundle_id`) ) TYPE=MyISAM; "; @mysql_query($sql_create); $sql = 'INSERT INTO `' . $tb . '` ' . $sql; @mysql_query($sql); $cache->save($rows, $cache_id); } } return $rows; }
Tell me if you’ve used any of these content caching methods before and how it’s worked for you?
Very useful
Thank you
This is rad! I used it to cache a Twitter feed similar to your related post, but I process the results before adding to a flat file. Then I made an action that can clear the cache on command… works like a charm!
If you are caching the complete page do it on web server/proxy level not in the application. It is faster.
Great post!
Do you know if it works with apache virtual include?
Thanks!