511 lines
13 KiB
ReStructuredText
511 lines
13 KiB
ReStructuredText
Caching
|
|
=======
|
|
|
|
Doctrine provides cache drivers in the ``Common`` package for some
|
|
of the most popular caching implementations such as APC, Memcache
|
|
and Xcache. We also provide an ``ArrayCache`` driver which stores
|
|
the data in a PHP array. Obviously, the cache does not live between
|
|
requests but this is useful for testing in a development
|
|
environment.
|
|
|
|
Cache Drivers
|
|
-------------
|
|
|
|
The cache drivers follow a simple interface that is defined in
|
|
``Doctrine\Common\Cache\Cache``. All the cache drivers extend a
|
|
base class ``Doctrine\Common\Cache\AbstractCache`` which implements
|
|
the before mentioned interface.
|
|
|
|
The interface defines the following methods for you to publicly
|
|
use.
|
|
|
|
|
|
- fetch($id) - Fetches an entry from the cache.
|
|
- contains($id) - Test if an entry exists in the cache.
|
|
- save($id, $data, $lifeTime = false) - Puts data into the cache.
|
|
- delete($id) - Deletes a cache entry.
|
|
|
|
Each driver extends the ``AbstractCache`` class which defines a few
|
|
abstract protected methods that each of the drivers must
|
|
implement.
|
|
|
|
|
|
- \_doFetch($id)
|
|
- \_doContains($id)
|
|
- \_doSave($id, $data, $lifeTime = false)
|
|
- \_doDelete($id)
|
|
|
|
The public methods ``fetch()``, ``contains()``, etc. utilize the
|
|
above protected methods that are implemented by the drivers. The
|
|
code is organized this way so that the protected methods in the
|
|
drivers do the raw interaction with the cache implementation and
|
|
the ``AbstractCache`` can build custom functionality on top of
|
|
these methods.
|
|
|
|
APC
|
|
~~~
|
|
|
|
In order to use the APC cache driver you must have it compiled and
|
|
enabled in your php.ini. You can read about APC
|
|
`in the PHP Documentation <http://us2.php.net/apc>`_. It will give
|
|
you a little background information about what it is and how you
|
|
can use it as well as how to install it.
|
|
|
|
Below is a simple example of how you could use the APC cache driver
|
|
by itself.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
|
|
$cacheDriver->save('cache_id', 'my_data');
|
|
|
|
Memcache
|
|
~~~~~~~~
|
|
|
|
In order to use the Memcache cache driver you must have it compiled
|
|
and enabled in your php.ini. You can read about Memcache
|
|
` on the PHP website <http://us2.php.net/memcache>`_. It will
|
|
give you a little background information about what it is and how
|
|
you can use it as well as how to install it.
|
|
|
|
Below is a simple example of how you could use the Memcache cache
|
|
driver by itself.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$memcache = new Memcache();
|
|
$memcache->connect('memcache_host', 11211);
|
|
|
|
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
|
|
$cacheDriver->setMemcache($memcache);
|
|
$cacheDriver->save('cache_id', 'my_data');
|
|
|
|
Xcache
|
|
~~~~~~
|
|
|
|
In order to use the Xcache cache driver you must have it compiled
|
|
and enabled in your php.ini. You can read about Xcache
|
|
`here <http://xcache.lighttpd.net/>`_. It will give you a little
|
|
background information about what it is and how you can use it as
|
|
well as how to install it.
|
|
|
|
Below is a simple example of how you could use the Xcache cache
|
|
driver by itself.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$cacheDriver = new \Doctrine\Common\Cache\XcacheCache();
|
|
$cacheDriver->save('cache_id', 'my_data');
|
|
|
|
Redis
|
|
~~~~~
|
|
|
|
In order to use the Redis cache driver you must have it compiled
|
|
and enabled in your php.ini. You can read about what is Redis
|
|
`from here <http://redis.io/>`_. Also check
|
|
`here <https://github.com/nicolasff/phpredis/>`_ for how you can use
|
|
and install Redis PHP extension.
|
|
|
|
Below is a simple example of how you could use the Redis cache
|
|
driver by itself.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$redis = new Redis();
|
|
$redis->connect('redis_host', 6379);
|
|
|
|
$cacheDriver = new \Doctrine\Common\Cache\RedisCache();
|
|
$cacheDriver->setRedis($redis);
|
|
$cacheDriver->save('cache_id', 'my_data');
|
|
|
|
Using Cache Drivers
|
|
-------------------
|
|
|
|
In this section we'll describe how you can fully utilize the API of
|
|
the cache drivers to save cache, check if some cache exists, fetch
|
|
the cached data and delete the cached data. We'll use the
|
|
``ArrayCache`` implementation as our example here.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$cacheDriver = new \Doctrine\Common\Cache\ArrayCache();
|
|
|
|
Saving
|
|
~~~~~~
|
|
|
|
To save some data to the cache driver it is as simple as using the
|
|
``save()`` method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$cacheDriver->save('cache_id', 'my_data');
|
|
|
|
The ``save()`` method accepts three arguments which are described
|
|
below.
|
|
|
|
|
|
- ``$id`` - The cache id
|
|
- ``$data`` - The cache entry/data.
|
|
- ``$lifeTime`` - The lifetime. If != false, sets a specific
|
|
lifetime for this cache entry (null => infinite lifeTime).
|
|
|
|
You can save any type of data whether it be a string, array,
|
|
object, etc.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$array = array(
|
|
'key1' => 'value1',
|
|
'key2' => 'value2'
|
|
);
|
|
$cacheDriver->save('my_array', $array);
|
|
|
|
Checking
|
|
~~~~~~~~
|
|
|
|
Checking whether some cache exists is very simple, just use the
|
|
``contains()`` method. It accepts a single argument which is the ID
|
|
of the cache entry.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
if ($cacheDriver->contains('cache_id')) {
|
|
echo 'cache exists';
|
|
} else {
|
|
echo 'cache does not exist';
|
|
}
|
|
|
|
Fetching
|
|
~~~~~~~~
|
|
|
|
Now if you want to retrieve some cache entry you can use the
|
|
``fetch()`` method. It also accepts a single argument just like
|
|
``contains()`` which is the ID of the cache entry.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$array = $cacheDriver->fetch('my_array');
|
|
|
|
Deleting
|
|
~~~~~~~~
|
|
|
|
As you might guess, deleting is just as easy as saving, checking
|
|
and fetching. We have a few ways to delete cache entries. You can
|
|
delete by an individual ID, regular expression, prefix, suffix or
|
|
you can delete all entries.
|
|
|
|
By Cache ID
|
|
^^^^^^^^^^^
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$cacheDriver->delete('my_array');
|
|
|
|
You can also pass wild cards to the ``delete()`` method and it will
|
|
return an array of IDs that were matched and deleted.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$deleted = $cacheDriver->delete('users_*');
|
|
|
|
By Regular Expression
|
|
^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
If you need a little more control than wild cards you can use a PHP
|
|
regular expression to delete cache entries.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$deleted = $cacheDriver->deleteByRegex('/users_.*/');
|
|
|
|
By Prefix
|
|
^^^^^^^^^
|
|
|
|
Because regular expressions are kind of slow, if simply deleting by
|
|
a prefix or suffix is sufficient, it is recommended that you do
|
|
that instead of using a regular expression because it will be much
|
|
faster if you have many cache entries.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$deleted = $cacheDriver->deleteByPrefix('users_');
|
|
|
|
By Suffix
|
|
^^^^^^^^^
|
|
|
|
Just like we did above with the prefix you can do the same with a
|
|
suffix.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$deleted = $cacheDriver->deleteBySuffix('_my_account');
|
|
|
|
All
|
|
^^^
|
|
|
|
If you simply want to delete all cache entries you can do so with
|
|
the ``deleteAll()`` method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$deleted = $cacheDriver->deleteAll();
|
|
|
|
Counting
|
|
~~~~~~~~
|
|
|
|
If you want to count how many entries are stored in the cache
|
|
driver instance you can use the ``count()`` method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
echo $cacheDriver->count();
|
|
|
|
.. note::
|
|
|
|
In order to use ``deleteByRegex()``, ``deleteByPrefix()``,
|
|
``deleteBySuffix()``, ``deleteAll()``, ``count()`` or ``getIds()``
|
|
you must enable an option for the cache driver to manage your cache
|
|
IDs internally. This is necessary because APC, Memcache, etc. don't
|
|
have any advanced functionality for fetching and deleting. We add
|
|
some functionality on top of the cache drivers to maintain an index
|
|
of all the IDs stored in the cache driver so that we can allow more
|
|
granular deleting operations.
|
|
|
|
::
|
|
|
|
<?php
|
|
$cacheDriver->setManageCacheIds(true);
|
|
|
|
|
|
Namespaces
|
|
~~~~~~~~~~
|
|
|
|
If you heavily use caching in your application and utilize it in
|
|
multiple parts of your application, or use it in different
|
|
applications on the same server you may have issues with cache
|
|
naming collisions. This can be worked around by using namespaces.
|
|
You can set the namespace a cache driver should use by using the
|
|
``setNamespace()`` method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$cacheDriver->setNamespace('my_namespace_');
|
|
|
|
Integrating with the ORM
|
|
------------------------
|
|
|
|
The Doctrine ORM package is tightly integrated with the cache
|
|
drivers to allow you to improve performance of various aspects of
|
|
Doctrine by just simply making some additional configurations and
|
|
method calls.
|
|
|
|
Query Cache
|
|
~~~~~~~~~~~
|
|
|
|
It is highly recommended that in a production environment you cache
|
|
the transformation of a DQL query to its SQL counterpart. It
|
|
doesn't make sense to do this parsing multiple times as it doesn't
|
|
change unless you alter the DQL query.
|
|
|
|
This can be done by configuring the query cache implementation to
|
|
use on your ORM configuration.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$config = new \Doctrine\ORM\Configuration();
|
|
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ApcCache());
|
|
|
|
Result Cache
|
|
~~~~~~~~~~~~
|
|
|
|
The result cache can be used to cache the results of your queries
|
|
so that we don't have to query the database or hydrate the data
|
|
again after the first time. You just need to configure the result
|
|
cache implementation.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$config->setResultCacheImpl(new \Doctrine\Common\Cache\ApcCache());
|
|
|
|
Now when you're executing DQL queries you can configure them to use
|
|
the result cache.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$query = $em->createQuery('select u from \Entities\User u');
|
|
$query->useResultCache(true);
|
|
|
|
You can also configure an individual query to use a different
|
|
result cache driver.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$query->setResultCacheDriver(new \Doctrine\Common\Cache\ApcCache());
|
|
|
|
.. note::
|
|
|
|
Setting the result cache driver on the query will
|
|
automatically enable the result cache for the query. If you want to
|
|
disable it pass false to ``useResultCache()``.
|
|
|
|
::
|
|
|
|
<?php
|
|
$query->useResultCache(false);
|
|
|
|
|
|
If you want to set the time the cache has to live you can use the
|
|
``setResultCacheLifetime()`` method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$query->setResultCacheLifetime(3600);
|
|
|
|
The ID used to store the result set cache is a hash which is
|
|
automatically generated for you if you don't set a custom ID
|
|
yourself with the ``setResultCacheId()`` method.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$query->setResultCacheId('my_custom_id');
|
|
|
|
You can also set the lifetime and cache ID by passing the values as
|
|
the second and third argument to ``useResultCache()``.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$query->useResultCache(true, 3600, 'my_custom_id');
|
|
|
|
Metadata Cache
|
|
~~~~~~~~~~~~~~
|
|
|
|
Your class metadata can be parsed from a few different sources like
|
|
YAML, XML, Annotations, etc. Instead of parsing this information on
|
|
each request we should cache it using one of the cache drivers.
|
|
|
|
Just like the query and result cache we need to configure it
|
|
first.
|
|
|
|
.. code-block:: php
|
|
|
|
<?php
|
|
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ApcCache());
|
|
|
|
Now the metadata information will only be parsed once and stored in
|
|
the cache driver.
|
|
|
|
Clearing the Cache
|
|
------------------
|
|
|
|
We've already shown you previously how you can use the API of the
|
|
cache drivers to manually delete cache entries. For your
|
|
convenience we offer a command line task for you to help you with
|
|
clearing the query, result and metadata cache.
|
|
|
|
From the Doctrine command line you can run the following command.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache
|
|
|
|
Running this task with no arguments will clear all the cache for
|
|
all the configured drivers. If you want to be more specific about
|
|
what you clear you can use the following options.
|
|
|
|
To clear the query cache use the ``--query`` option.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --query
|
|
|
|
To clear the metadata cache use the ``--metadata`` option.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --metadata
|
|
|
|
To clear the result cache use the ``--result`` option.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --result
|
|
|
|
When you use the ``--result`` option you can use some other options
|
|
to be more specific about what queries result sets you want to
|
|
clear.
|
|
|
|
Just like the API of the cache drivers you can clear based on an
|
|
ID, regular expression, prefix or suffix.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --result --id=cache_id
|
|
|
|
Or if you want to clear based on a regular expressions.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --result --regex=users_.*
|
|
|
|
Or with a prefix.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --result --prefix=users_
|
|
|
|
And finally with a suffix.
|
|
|
|
.. code-block:: php
|
|
|
|
$ ./doctrine clear-cache --result --suffix=_my_account
|
|
|
|
.. note::
|
|
|
|
Using the ``--id``, ``--regex``, etc. options with the
|
|
``--query`` and ``--metadata`` are not allowed as it is not
|
|
necessary to be specific about what you clear. You only ever need
|
|
to completely clear the cache to remove stale entries.
|
|
|
|
|
|
Cache Slams
|
|
-----------
|
|
|
|
Something to be careful of when utilizing the cache drivers is
|
|
cache slams. If you have a heavily trafficked website with some
|
|
code that checks for the existence of a cache record and if it does
|
|
not exist it generates the information and saves it to the cache.
|
|
Now if 100 requests were issued all at the same time and each one
|
|
sees the cache does not exist and they all try and insert the same
|
|
cache entry it could lock up APC, Xcache, etc. and cause problems.
|
|
Ways exist to work around this, like pre-populating your cache and
|
|
not letting your users requests populate the cache.
|
|
|
|
You can read more about cache slams
|
|
`in this blog post <http://notmysock.org/blog/php/user-cache-timebomb.html>`_.
|
|
|
|
|