Улучшение производительности с помощью кэширования

Phalcon предоставляет класс Phalcon\Cache, дающий быстрый доступ к часто используемым или уже сгенерированным данным. Phalcon\Cache написан на языке C, поэтому он предоставляет высокую производительность и пониженный расход ресурсов. Этот класс использует два компонента: frontend и backend. Frontend компонент является входным источником или интерфейсом, в то время как backend предоставляет опции хранения данных.

Где применять кэширование?

Хотя этот компонент очень быстрый, его реализация в случаях, которые не нужны, может привести к потере производительности, а не к увеличению. Мы рекомендуем вам проверять эти случаи перед использованием кеша:

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

Поведение системы кэширования

Процесс кэширования разделена в 2 части:

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

Factory

Создание фронтэнд и бэкэнд адаптеров может быть достигнуто двумя способами:

Традиционный путь

<?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/',
    ]
);

или используя объект Factory, следующим образом:

<?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);

Кэширование выходных фрагментов

Выходные фрагменты — это части HTML или текста, которые кэшируются “как есть” и возвращаются “как есть”. Выходные данные автоматически захватываются из ob_* функции или из выходного потока PHP и сохраняются в кэш. Следующий пример демонстрирует такое использование. Он получает сгенерированные выходные данные и сохраняет их в файл. Кэш обновляется каждые 172800 секунд (двое суток).

Реализация этого механизма позволяет нам повысить производительность за счет исключения работы помощника Phalcon\Tag::linkTo(), который вызывается каждый раз в этом участке кода.

<?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;
}

Кэширование произвольных данных

Кэширование только данных одинаково важно для вашего приложения. Кэширование позволяет снизить нагрузку на базы данных за счет повторного использования обычно используемых (но не обновленных) данных, что ускоряет ваше приложение.

Пример файлового бэкенда

Существует файловый адаптер кэширования (File). Единственным параметром для него является место, где будут храниться закэшированные файлы. Это контролируется параметром cacheDir, который должен содержать завершающий слеш.

<?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';
}

Пример использования бэкэнда Memcache

Для этого нам достаточно немного изменить вышестоящий пример. В частности изменится конфигурация.

<?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';
}

Запрос данных из кэша

Все элементы добавляемые в кэш идентифицируются по ключам. В случае с файловым бэкэндом, ключом является название файла. Для получения данных из кэша нам необходимо выполнить запрос к кэшу с указанием уникального ключа. Если ключа не существует, метод вернет значение NULL.

<?php

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

Для того чтобы узнать какие ключи сейчас хранятся можно выполнить метод queryKeys:

<?php

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

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

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

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

Удаление данных из кэша

Иногда вам нужно принудительно аннулировать запись в кэш (из-за обновления в кэшированных данных). Единственное требование - знать ключ, с которым были сохранены данные.

<?php

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

$keys = $cache->queryKeys();

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

Проверка существования кэша

Существует возможность проверить наличие данных в кэше:

<?php

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

Время жизни

lifetime — это время, исчисляемое в секундах, которое означает, сколько будут храниться данные в бэкэнде. По умолчанию все данные получают “время жизни”, которое было указано при создании фронтэнд компонента. Вы можете указать другое значение при сохранении или получении данных из кэша:

Задаем время жизни при получении:

<?php

$cacheKey = 'my.cache';

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

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

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

Задаем время жизни при сохранении:

<?php

$cacheKey = 'my.cache';

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

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

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

Многоуровневое кэширование

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

<?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);

Фронтэнд адаптеры

Доступные фронтэнд адаптеры приведены в таблице:

АдаптерОписание
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 Используется для кэширования любых типов данных без сериализации.

Реализация собственных фронтэнд адаптеров

Для создания фронтэнд адаптера необходимо реализовать интерфейс Phalcon\Cache\FrontendInterface.

Бэкенд адаптеры

Доступные бэкэнд адаптеры приведены в таблице:

АдаптерОписаниеИнформацияНеобходимые расширения
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 сервере. Memcache Memcache
Phalcon\Cache\Backend\Mongo Сохраняет данные в базе данных Mongo. MongoDB Mongo
Phalcon\Cache\Backend\Redis Сохраняет данные в Redis. Redis Redis
Phalcon\Cache\Backend\Xcache Сохраняет данные в XCache. XCache XCache

Фабрика

Существует множество бэкенд адаптеров (см. Бэкенд адаптеры). Используемый вами, будет зависеть от нужд вашего приложения. Следующий пример демонстрирует инициализацию бэкенд адаптера с использованием опции adapter. Если опция frontend будет представлена в виде массива, фронтэнд адаптер будет инициализирован при помощи соответствующей фабрики.

<?php

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

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

Реализация собственных бэкэнд адаптеров

Для создания бэкэнд адаптера необходимо реализовать интерфейс Phalcon\Cache\BackendInterface.

Параметры файлового бэкэнда

Этот бэкэнд будет хранить кешированный контент в файлы на локальном сервере. Доступными опциями для этого бэкэнд являются:

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

Параметры Libmemcached бэкэнда

Этот бэкэнд будет хранить кешированный контент на сервере memcached. По умолчанию используются постоянные пулы соединений memcached. Доступными опциями для этого бэкэнд являются:

Общие параметры

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

Параметры сервера

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

Параметры клиента

Используется для установки параметров Memcached. Подробнее см. Memcached::setOptions.

Пример

<?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 бэкэнда

Этот бэкэнд будет хранить кешированный контент на сервере memcached. Доступными опциями для этого бэкэнд являются:

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

Параметры APC бэкэнда

Этот бэкэнд будет хранить кешированный контент в Alternative PHP Cache (APC). Доступными опциями для этого бэкэнд являются:

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

Параметры бэкэнд APCU

Этот бэкэнд будет хранить кешированный контент в Alternative PHP Cache (APCU). Доступными опциями для этого бэкэнд являются:

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

Параметры Mongo бэкэнда

Этот бэкэнд будет хранить кешированный контент на сервере MongoDB (MongoDB). Доступными опциями для этого бэкэнд являются:

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

Параметры XCache бэкэнда

Этот бэкэнд будет хранить кешированный контент на XCache (XCache). Доступными опциями для этого бэкэнд являются:

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

Redis Backend Options

Этот бэкэнд будет хранить кешированный контент на сервере Redis (Redis). Доступными опциями для этого бэкэнд являются:

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

Для этих компонентов доступно больше адаптеров. Phalcon Incubator