缓存JSON:Apache,PHP,jQuery

I'm trying to cache JSON content generated by a php script from database. However the dataset is very stable, there are very few changes or additions. Meaning data could go unchanged for weeks. The issue is that it contains a LOB column and that just takes a noticeable time to load, longer compared to supplying the json from a text file meaning git is the actual database call that makes it slow.

I'm displaying the data in a table with pagination(datatables jquery plugin) and for each page change the data is fetched from database again, also when going back to the previous page.

I've tried following:

"beforeSend": function (request)
{
    request.setRequestHeader("cache-control", "max-age=86400");
},

Does not work.

I tried mod_expires:

ExpiresActive On
ExpiresDefault "access plus 4 hours"
ExpiresByType application/javascript "access plus 1 day"
ExpiresByType application/json "access plus 1 day"

Does not work.

Therefore I assume all these settings are for real files on the file system only and not for dynamic generated stuff?

I would prefer a configurable approach meaning using Apache/PHP since I will not have full control over the server.

EDIT BEFORE FIRST ANSWER:

Note that the JSON contains multiple records so a key/value store would be kind of difficult to avhieve. The key would have to contain a lot of stuff: Query/filter expression and the requested page for paging.

EDIT 2:

Development and prod. are Windows... so memcached is not really an option...

EDIT 3:

I've tried kristovaher solution but does not work. The cache headers are not in the response all the time and after some plaing around I believe I determined the issue: I'm required do use NTLM authentication and when doing 2 request shortly after each other it works fine, however if you wait a bit, it seems the user is re-authenticated and then the cache control header is "lost".

I've played around a bit and come to the conclusion that caching with client side and using headers does not seem to work in my case. Maybe I'm doing it wrong, maybe it is the way my application works or web server config.

Anyway my solution was to use APC:

http://www.php.net/manual/en/book.apc.php

I'm using windows, the appropriate binaries can be found here:

http://downloads.php.net/pierre/

which one depends on your PHP version and how it was compiled (with vc6 or vc9). The php_apc.dll will need to be put in your php extension directory and you will need to add the line

extension=php_apc.dll 

to the php.ini

Then you bascially do:

if (apc_exists($key)){
   return apc_fetch($key);
}
// get data from database because it was not in the cache
//...
//add data to cache
apc_add($key, $result);

If data on my page is uncached it took around 1-2s to load. Well thats not bad but feels very laggy. if data is in cache it is more like 20-30ms. of course this difference is very noticeable.

Cache-control is a response header. (edit: It is actually also a request header. Thanks for Gumbo for pointing it out.) You need to add such headers in the response (data that PHP sends).

<?php
// How long my cache should last
$cacheDuration=300; // in seconds
// Client is told to cache these results for set duration
header('Cache-Control: public,max-age='.$cacheDuration.',must-revalidate');
header('Expires: '.gmdate('D, d M Y H:i:s',($_SERVER['REQUEST_TIME']+$cacheDuration)).' GMT');
header('Last-modified: '.gmdate('D, d M Y H:i:s',$_SERVER['REQUEST_TIME']).' GMT');

// Pragma header removed should the server happen to set it automatically
// Pragma headers can make browser misbehave and still ask data from server
header_remove('Pragma');
?>

You should actually do it better than that however. Cache-control headers only tell a single client how to maintain that data, another user might still make your server re-do this whole thing. This is what a smart cache system would look like:

  • You generate your JSON, whatever it is.
  • You store that JSON in your filesystem as a temporary file.
  • You send the contents to client with the Cache-control headers, making the browser not request the data from the server again and use cache instead.
  • If another user comes to the site and their browser does not have that cache, your script should check if the cache file (that you created in point 2) exists, if it exists then you read data to the client from that file instead of generating it again.
  • You should also check how old the cache file is, maybe generate the file again every few days.

But there are a lot of things you need to pay attention to when doing this, if your cache includes sensitive information then you have to make sure that this is not forwarded to wrong clients from cache.

EDIT:

Live example is here: http://waher.net/cachetest.php (the same code as shown above).

This file is loaded by browser from cache for 5 minutes. But note that if you refresh the page, it ignores browser cache and still makes a request to the server. You can try it better if you open a new tab instead of refresh the page and enter the URL and Firebug/Chrome Dev Tools will show that the file was loaded from cache.