views:

157

answers:

4

Hi Folks,

i think iam getting crazy, iam trying to implement Zend_Cache to cache my database query. I know how it works and how to configure.

But i cant find a good way to set the Identifier for the cache entrys. I have an method wich search for records in my database (based on an array with search values).

/**
 * Find Record(s)
 * Returns one record, or array with objects
 *
 * @param array   $search Search columns => value
 * @param integer $limit  Limit results
 * @return array One record , or array with objects
 */
public function find(array $search, $limit = null)
{
    $identifier = 'NoIdea';

    if (!($data = $this->_cache->load($identifier))) {
        // fetch
        // save to cache with $identifier..
    }

But what kind of identifier can use in this situation?

+3  A: 

The identifier should be a unique string to represent the cache entry.

You might like to name them based on your database tables and fields, e.g. db_tablename_primarykey_1

If you have a method with several arguments, it'll be a pain to make a string containing all of them. Alternatively you can concat a function's arguments and the db_tablename to produce a string for hashing. For example:

$identifier = md5('db_tablename_find' . serialize(func_get_args()));
David Caunt
A: 

What happens when your underlying data changes? Well, all applicable cache records should be scrapped. That's a mondo can of worms!

How about offloading this data caching to your RDBMS?

For example, MySQL Query Cache handles this for you, invalidating stale cache records as appropriate.

Derek Illchuk
The point of Zend_Cache is to remove load from the database, using a memcache or APC backend. Simply retrieving data by key is much faster than waiting for locked tables or relatively complex queries to complete. This does of course mean that more time is spent in developing your application, as you have to purge the cache when data is changed. This is the balance! Overall, there are big gains to be made - this is the reason why top websites cache data!
David Caunt
OK, but which cache IDs would you clear with your serialize(func_get_args()) when some piece of data changes? As is, there's no way to know. So, either improve your method, or use the caching on the database side. We're arguing but neither of us has given a usable answer!
Derek Illchuk
Zend_Cache supports tagging, so you can tag records and then remove them as appropriate. Alternatively you can use a short TTL for data. What I'm trying to say is that there are different ways to cache things in different circumstances. Caching is VERY application specific. You raise a very valid point about being able to remove stale records! The query cache can be used in addition to this strategy.
David Caunt
+1  A: 

You may want to consider using Function frontend for Zend Cache, where you can do something like this:

$cache->call('veryExpensiveFunc', $params);

and the key would be produced automatically (could be a long-ish one, though). You may use Class frontend which would allow you to cache all methods of the class, transparently.

StasM
+1  A: 

I'm having this same issue. I don't know if you found a solution yet but i have a temporary solution (because it's not very efficient):

  1. Create a cache directory for each table you are going to query and cache results.

  2. Whenever you query that table, save the result using a unique id. Here's the problem with my solution. You can't use the query as an id since it may be too long and some operating systems may reject it. So what i came up with is to store all queries in another file along with an auto-incrementing id, like this:

0->>SELECT * FROM table 1->>SELECT * FROM table WHERE foo = bar

You get the idea. So before executing the query check that file to see if the current query is there, if present, get the id and load using that id. You can store this file in the same cache directory.

  1. Whenever you update, insert or delete data in that table simply clean the cached records like this: $cache->clean('all'). You don't need to delete the id's file since the same queries will be run. And in case you are wondering, it will only clean cache files in that directory, hence the need for a directory for every table.

The good. You don't even have to set a lifetime (they can exist forever) or autoclean for ur cache since u clean it yourself (in code of course).

The ugly. Any test, load or save will make two (or three, when we need to save a new id for a new query) requests to the file system.

I've used this and it works fine, using Zend_Cache_Core and Zend_Cache_Backend_File. I'm not using the full framework, but i use the Zend_Cache module so if you use the whole framework you may need to create an abstract model class that will be doing the creating of directories, id files, and caching for your model child classes (which will then extend it).

I've seen here that using md5 may be a solution to having to create an id's file for every table. I'll try it out and get back to you. I don't know how this would work with Apc.

Joey
I've tried md5 hashing and it's working. So in step 2 where you create an id's file all you have to do in its stead is this:<code> //returns id for a given query function getCacheId($query) { return md5($query); }</code>To make the md5 hash more robust you can salt it (maybe with the name of the table).<code> //returns id for a given query function getCacheId($query, $table) { return md5($table . $query); }</code>
Joey