Sections

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

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

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

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, с помощью соответствующих инструментов, предоставляемыми этими приложениями.

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

Процесс кэширования разделена в 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;
}

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

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

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). Единственным параметром для него является место, где будут храниться закэшированные файлы. Это контролируется параметром 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';
}

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

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

Все элементы добавляемые в кэш идентифицируются по ключам. В случае с файловым бэкэндом, ключом является название файла. Для получения данных из кэша нам необходимо выполнить запрос к кэшу с указанием уникального ключа. Если ключа не существует, метод вернет значение 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');

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

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

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

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

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

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

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

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

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

Адаптер Описание Информация Необходимые расширения
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\Memory Stores data in memory    
Phalcon\Cache\Backend\Mongo Сохраняет данные в базе данных Mongo. MongoDB Mongo
Phalcon\Cache\Backend\Redis Сохраняет данные в Redis. Redis Redis
Phalcon\Cache\Backend\Xcache Сохраняет данные в XCache. XCache XCache
ПРИМЕЧАНИЕ В PHP 7 чтобы использовать классы адаптеров на базе phalcon apc вам нужно установить apcu и apcu_bc пакеты из pecl. Now in Phalcon 3.4.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}

Фабрика

Существует множество бэкенд адаптеров (см. Бэкенд адаптеры). Используемый вами, будет зависеть от нужд вашего приложения. Следующий пример демонстрирует инициализацию бэкенд адаптера с использованием опции 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);

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

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

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

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

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

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

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:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

Доступны и другие адаптеры для этих компонентов в репозитории Phalcon Incubator