This article reflects v3.4 and has not yet been revised

Improving Performance with Cache

Phalcon provides the Phalcon\Cache class allowing faster access to frequently used or already processed data. Phalcon\Cache is written in C, achieving higher performance and reducing the overhead when getting items from the backends. This class uses an internal structure of frontend and backend components. Front-end components act as input sources or interfaces, while backend components offer storage options to the class.

When to implement cache?

Although this component is very fast, implementing it in cases that are not needed could lead to a loss of performance rather than gain. We recommend you check this cases before using a cache:

  • Вы делаете сложные расчеты, которые каждый раз возвращают один и тот же результат (или результат редко изменяется)
  • Вы используете много хелперов и результат генерации почти всегда одинаковый
  • Вы постоянно обращаетесь к базе данных и редко изменяете эти данные

Примечание Даже после реализации кэширования, вы должны проверить коэффициент попадания запросов в кэш. Это можно легко проверить, особенно используя Memcache или Apc, с помощью соответствующих инструментов, предоставляемыми этими приложениями.

Caching Behavior

The caching process is divided into 2 parts:

  • Frontend: Эта часть отвечает за проверку времени жизни ключа и выполняет дополнительные преобразования над данными, до операции сохранения или извлечения их из backend
  • Backend: Эта часть отвечает за коммуникацию, запись/чтение данных по запросу frontend.

Factory

Instantiating frontend or backend adapters can be achieved by two ways:

Traditional way

<?php

use Phalcon\Cache\Backend\File as BackFile;
use Phalcon\Cache\Frontend\Data as FrontData;

// Создание frontend для выходных данных. Кэширование файлов на 2 дня
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// Создаем компонент, который будем кэшировать из "Выходных данных" в "Файл"
// Устанавливаем папку для кэшируемых файлов - важно указать символ "/" в конце пути
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

or using the Factory object as follows:

<?php

use Phalcon\Cache\Frontend\Factory as FFactory;
use Phalcon\Cache\Backend\Factory as BFactory;

 $options = [
     'lifetime' => 172800,
     'adapter'  => 'data',
 ];
 $frontendCache = FFactory::load($options);


$options = [
    'cacheDir' => '../app/cache/',
    'prefix'   => 'app-data',
    'frontend' => $frontendCache,
    'adapter'  => 'file',
];

$backendCache = BFactory::load($options);

Caching Output Fragments

An output fragment is a piece of HTML or text that is cached as is and returned as is. The output is automatically captured from the ob_* functions or the PHP output so that it can be saved in the cache. The following example demonstrates such usage. It receives the output generated by PHP and stores it into a file. The contents of the file are refreshed every 172,800 seconds (2 days).

The implementation of this caching mechanism allows us to gain performance by not executing the helper Phalcon\Tag::linkTo() call whenever this piece of code is called.

<?php

use Phalcon\Tag;
use Phalcon\Cache\Backend\File as BackFile;
use Phalcon\Cache\Frontend\Output as FrontOutput;

// Создание frontend для выходных данных. Кэшируем файлы на двое суток
$frontCache = new FrontOutput(
    [
        'lifetime' => 172800,
    ]
);

// Создаем компонент, который будем кэшировать из "Выходных данных"
// в файловый бэкэнд.
// Устанавливаем папку для кэшируемых файлов - важно указать символ '/'
// в конце пути
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

// Получить/Создать кэшируемый файл ../app/cache/my-cache.html
$content = $cache->start('my-cache.html');

// Если $content является значением NULL,
// значит данных в кэше нет и их надо сгенерировать
if ($content === null) {
    // Выводим дату и время
    echo date('r');

    // Генерируем ссылку на "регистрацию"
    echo Tag::linkTo(
        [
            'user/signup',
            'Sign Up',
            'class' => 'signup-button',
        ]
    );

    // Сохраняем вывод в кэш
    $cache->save();
} else {
    // Ввыводим кэшируемые данные
    echo $content;
}

Примечание В этом примере наш код остается таким же и выводит те же данные пользователю. Наш компонент кэширования прозрачно перехватывает вывод и сохраняет его в кэшируемый файл (когда кэш сгенерирован) или он отправляет уже готовые данные обратно к пользователю, а это естественно позволяет экономить на выполнении операций.

Caching Arbitrary Data

Caching just data is equally important for your application. Caching can reduce database load by reusing commonly used (but not updated) data, thus speeding up your application.

File Backend Example

One of the caching adapters is File. The only key area for this adapter is the location of where the cache files will be stored. This is controlled by the cacheDir option which must have a backslash at the end of it.

<?php

use Phalcon\Cache\Backend\File as BackFile;
use Phalcon\Cache\Frontend\Data as FrontData;

// Кэшируем данные на двое суток
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// Создаем компонент, который будем кэшировать из "Выходных данных" в "Файл"
// Устанавливаем папку для кэшируемых файлов
// Важно сохранить символ "/" в конце пути
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

$cacheKey = 'robots_order_id.cache';

// Пробуем получить закэшированные записи
$robots = $cache->get($cacheKey);

if ($robots === null) {
    // $robots может иметь значение NULL из-за того, что истекло время жизни
    // или данных просто не существует. Получим данные из БД
    $robots = Robots::find(
        [
            'order' => 'id',
        ]
    );

    // Сохраняем их в кэше
    $cache->save($cacheKey, $robots);
}

// Используем $robots :)
foreach ($robots as $robot) {
   echo $robot->name, '\n';
}

Memcached Backend Example

The above example changes slightly (especially in terms of configuration) when we are using a Memcached backend.

<?php

use Phalcon\Cache\Frontend\Data as FrontData;
use Phalcon\Cache\Backend\Libmemcached as BackMemCached;

// Кэшируем данные на 1 час
$frontCache = new FrontData(
    [
        'lifetime' => 3600,
    ]
);

// Создаем компонент, который будет кэшировать данные в Memcache
// Настройки подключения к Memcache
$cache = new BackMemCached(
    $frontCache,
    [
        'servers' => [
            [
                'host'   => '127.0.0.1',
                'port'   => '11211',
                'weight' => '1',
            ]
        ]
    ]
);

$cacheKey = 'robots_order_id.cache';

// Пробуем получить закэшированные записи
$robots = $cache->get($cacheKey);

if ($robots === null) {
    // $robots может иметь значение NULL из-за того, что истекло время жизни
    // или данных просто не существует. Получим данные из БД
    $robots = Robots::find(
        [
            'order' => 'id',
        ]
    );

    // Сохраняем их в кэше
    $cache->save($cacheKey, $robots);
}

// Используем $robots :)
foreach ($robots as $robot) {
   echo $robot->name, '\n';
}

ПРИМЕЧАНИЕ Вызов save() возвращает логическое значение, указывающее успех (true) или сбой (false). В зависимости от бэкэнда, который вы используете, вам понадобится обратится к соответствующим логам, для выявления сбоев.

Querying the cache

The elements added to the cache are uniquely identified by a key. In the case of the File backend, the key is the actual filename. To retrieve data from the cache, we just have to call it using the unique key. If the key does not exist, the get method will return null.

<?php

// Получаем продукты по ключу "myProducts"
$products = $cache->get('myProducts');

If you want to know which keys are stored in the cache you could call the queryKeys method:

<?php

// Получаем все ключи, которые хранятся в кэше
$keys = $cache->queryKeys();

foreach ($keys as $key) {
    $data = $cache->get($key);

    echo 'Key=', $key, ' Data=', $data;
}

// Получаем все ключи, которые начинаются с префикса "my-prefix"
$keys = $cache->queryKeys('my-prefix');

Deleting data from the cache

There are times where you will need to forcibly invalidate a cache entry (due to an update in the cached data). The only requirement is to know the key that the data have been stored with.

<?php

// Удаляем элемент по определенному ключу
$cache->delete('someKey');

$keys = $cache->queryKeys();

// Удаляем все из кэша
foreach ($keys as $key) {
    $cache->delete($key);
}

Checking cache existence

It is possible to check if a cache already exists with a given key:

<?php

if ($cache->exists('someKey')) {
    echo $cache->get('someKey');
} else {
    echo 'Данных в кэше не существует!';
}

Lifetime

A lifetime is a time in seconds that a cache could live without expire. By default, all the created caches use the lifetime set in the frontend creation. You can set a specific lifetime in the creation or retrieving of the data from the cache:

Setting the lifetime when retrieving:

<?php

$cacheKey = 'my.cache';

// Получаем кэш и задаем время жизни
$robots = $cache->get($cacheKey, 3600);

if ($robots === null) {
    $robots = 'some robots';

    // Сохраняем в кэше
    $cache->save($cacheKey, $robots);
}

Setting the lifetime when saving:

<?php

$cacheKey = 'my.cache';

$robots = $cache->get($cacheKey);

if ($robots === null) {
    $robots = 'some robots';

    // Задаем время жизни, сохраняя данные
    $cache->save($cacheKey, $robots, 3600);
}

Multi-Level Cache

This feature of the cache component, allows the developer to implement a multi-level cache. This new feature is very useful because you can save the same data in several cache locations with different lifetimes, reading first from the one with the faster adapter and ending with the slowest one until the data expires:

<?php

use Phalcon\Cache\Multiple;
use Phalcon\Cache\Backend\Apc as ApcCache;
use Phalcon\Cache\Backend\File as FileCache;
use Phalcon\Cache\Frontend\Data as DataFrontend;
use Phalcon\Cache\Backend\Memcache as MemcacheCache;

$ultraFastFrontend = new DataFrontend(
    [
        'lifetime' => 3600,
    ]
);

$fastFrontend = new DataFrontend(
    [
        'lifetime' => 86400,
    ]
);

$slowFrontend = new DataFrontend(
    [
        'lifetime' => 604800,
    ]
);

// Бэкэнды от самого быстрого до самого медленного
$cache = new Multiple(
    [
        new ApcCache(
            $ultraFastFrontend,
            [
                'prefix' => 'cache',
            ]
        ),
        new MemcacheCache(
            $fastFrontend,
            [
                'prefix' => 'cache',
                'host'   => 'localhost',
                'port'   => '11211',
            ]
        ),
        new FileCache(
            $slowFrontend,
            [
                'prefix'   => 'cache',
                'cacheDir' => '../app/cache/',
            ]
        ),
    ]
);

// Сохраняем, сохраняется сразу во все бэкэнды
$cache->save('my-key', $data);

Frontend Adapters

The available frontend adapters that are used as interfaces or input sources to the cache are:

Адаптер Описание
Phalcon\Cache\Frontend\Output Считывает данные из стандартного PHP вывода.
Phalcon\Cache\Frontend\Data It’s used to cache any kind of PHP data (big arrays, objects, text, etc). Data is serialized before stored in the backend.
Phalcon\Cache\Frontend\Base64 It’s used to cache binary data. The data is serialized using base64_encode before be stored in the backend.
Phalcon\Cache\Frontend\Json Data is encoded in JSON before be stored in the backend. Decoded after be retrieved. This frontend is useful to share data with other languages or frameworks.
Phalcon\Cache\Frontend\Igbinary It’s used to cache any kind of PHP data (big arrays, objects, text, etc). Data is serialized using Igbinary before be stored in the backend.
Phalcon\Cache\Frontend\None Используется для кэширования любых типов данных без сериализации.

Implementing your own Frontend adapters

The Phalcon\Cache\FrontendInterface interface must be implemented in order to create your own frontend adapters or extend the existing ones.

Backend Adapters

The backend adapters available to store cache data are:

Адаптер Описание Информация Необходимые расширения
Phalcon\Cache\Backend\Apc Сохраняет данные в Alternative PHP Cache (APC). APC APC
Phalcon\Cache\Backend\Apcu Сохраняет данные в APCu (APC без кеширования опкода). APCu APCu
Phalcon\Cache\Backend\File Сохраняет данные в локальный текстовый файл.    
Phalcon\Cache\Backend\Libmemcached Сохраняет данные на memcached сервере с использованием memcached расширения. Memcached Memcached
Phalcon\Cache\Backend\Memcache Сохраняет данные на memcached сервере с использованием memcached расширения. Memcache Memcache
Phalcon\Cache\Backend\Memory Stores data in memory    
Phalcon\Cache\Backend\Mongo Stores data to Mongo Database. MongoDB Mongo
Phalcon\Cache\Backend\Redis Stores data in Redis. Redis Redis
Phalcon\Cache\Backend\Xcache Stores data in XCache. XCache XCache
NOTE In PHP 7 to use phalcon apc based adapter classes you needed to install apcu and apcu_bc package from pecl. Now in Phalcon 4.0.0 you can switch your <em>\Apc classes to </em>\Apcu and remove apcu_bc. Keep in mind that in Phalcon 4 we will most likely remove all *\Apc classes. {.alert.alert-warning}

Factory

There are many backend adapters (see Backend Adapters). Используемый вами, будет зависеть от нужд вашего приложения. The following example loads the Backend Cache Adapter class using adapter option, if frontend will be provided as array it will call Frontend Cache Factory

<?php

use Phalcon\Cache\Backend\Factory;
use Phalcon\Cache\Frontend\Data;

$options = [
    'prefix'   => 'app-data',
    'frontend' => new Data(),
    'adapter'  => 'apc',
];
$backendCache = Factory::load($options);

Implementing your own Backend adapters

The Phalcon\Cache\BackendInterface interface must be implemented in order to create your own backend adapters or extend the existing ones.

File Backend Options

This backend will store cached content into files in the local server. The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.
cacheDir Папка с правами на запись, в которую будут сохраняться кэшируемые файлы.

Libmemcached Backend Options

This backend will store cached content on a memcached server. Per default persistent memcached connection pools are used. The available options for this backend are:

General options

Параметр Описание
statsKey Используется для отслеживания ключей кэша.
prefix Префикс, который будет автоматически добавляться к ключам кэша.
persistent_id Для создания экземпляра, который сохраняется между запросами, необходимо использовать persistent_id, чтобы указать уникальный идентификатор для экземпляра.

Servers options

Параметр Описание
host Хост memcached сервера.
port Порт memcached сервера.
weight Весовой коэффициент для заданного сервера по отношению к общему весу всех серверов в пуле.

Client options

Used for setting Memcached options. See Memcached::setOptions for more.

Пример

<?php
use Phalcon\Cache\Backend\Libmemcached;
use Phalcon\Cache\Frontend\Data as FrontData;

// Кэшируем данные на двое суток
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// Инициализация Libmemcached бэкэнда
$cache = new Libmemcached(
    $frontCache,
    [
        'servers' => [
            [
                'host'   => '127.0.0.1',
                'port'   => 11211,
                'weight' => 1,
            ],
        ],
        'client' => [
            \Memcached::OPT_HASH       => \Memcached::HASH_MD5,
            \Memcached::OPT_PREFIX_KEY => 'prefix.',
        ],
        'persistent_id' => 'my_app_cache',
    ]
);

Memcache Backend Options

This backend will store cached content on a memcached server. The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.
host Хост memcached сервера.
port Порт memcached сервера.
persistent Использовать постоянное соединение к серверу Memcached.

APC Backend Options

This backend will store cached content on Alternative PHP Cache (APC). The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.

APCU Backend Options

This backend will store cached content on Alternative PHP Cache (APCU). The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.

Mongo Backend Options

This backend will store cached content on a MongoDB server (MongoDB). The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.
server Строка подключения к MongoDB.
db Название базы данных.
collection Коллекция в базе данных.

XCache Backend Options

This backend will store cached content on XCache (XCache). The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.

Redis Backend Options

This backend will store cached content on a Redis server (Redis). The available options for this backend are:

Параметр Описание
prefix Префикс, который будет автоматически добавляться к ключам кэша.
host Хост Redis сервера.
port Порт Redis сервера.
auth Пароль для авторизации на защищенном паролем Redis сервере.
persistent Создавать постоянное соединение к Redis серверу.
index Индекс для использования в базе данных Redis.

There are more adapters available for this components in the Phalcon Incubator