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:

  • <0Frontend</strong>: 这一部分是负责检查,如果密钥已过期,并且执行更多转换对数据存储之前和之后从后端-检索
  • Backend: 这部分是负责沟通,写/读前端所需的数据。

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;

// Create an Output frontend. Cache the files for 2 days
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// Create the component that will cache from the 'Output' to a 'File' backend
// Set the cache file directory - it's important to keep the '/' at the end of
// the value for the folder
$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;

// Create an Output frontend. Cache the files for 2 days
$frontCache = new FrontOutput(
    [
        'lifetime' => 172800,
    ]
);

// Create the component that will cache from the 'Output' to a 'File' backend
// Set the cache file directory - it's important to keep the '/' at the end of
// the value for the folder
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

// Get/Set the cache file to ../app/cache/my-cache.html
$content = $cache->start('my-cache.html');

// If $content is null then the content will be generated for the cache
if ($content === null) {
    // Print date and time
    echo date('r');

    // Generate a link to the sign-up action
    echo Tag::linkTo(
        [
            'user/signup',
            'Sign Up',
            'class' => 'signup-button',
        ]
    );

    // Store the output into the cache file
    $cache->save();
} else {
    // Echo the cached output
    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;

// Cache the files for 2 days using a Data frontend
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// Create the component that will cache 'Data' to a 'File' backend
// Set the cache file directory - important to keep the `/` at the end of
// the value for the folder
$cache = new BackFile(
    $frontCache,
    [
        'cacheDir' => '../app/cache/',
    ]
);

$cacheKey = 'robots_order_id.cache';

// Try to get cached records
$robots = $cache->get($cacheKey);

if ($robots === null) {
    // $robots is null because of cache expiration or data does not exist
    // Make the database call and populate the variable
    $robots = Robots::find(
        [
            'order' => 'id',
        ]
    );

    // Store it in the cache
    $cache->save($cacheKey, $robots);
}

// Use $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;

// Cache data for one hour
$frontCache = new FrontData(
    [
        'lifetime' => 3600,
    ]
);

// Create the component that will cache 'Data' to a 'Memcached' backend
// Memcached connection settings
$cache = new BackMemCached(
    $frontCache,
    [
        'servers' => [
            [
                'host'   => '127.0.0.1',
                'port'   => '11211',
                'weight' => '1',
            ]
        ]
    ]
);

$cacheKey = 'robots_order_id.cache';

// Try to get cached records
$robots = $cache->get($cacheKey);

if ($robots === null) {
    // $robots is null because of cache expiration or data does not exist
    // Make the database call and populate the variable
    $robots = Robots::find(
        [
            'order' => 'id',
        ]
    );

    // Store it in the cache
    $cache->save($cacheKey, $robots);
}

// Use $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

// Retrieve products by key 'myProducts'
$products = $cache->get('myProducts');

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

<?php

// Query all keys used in the cache
$keys = $cache->queryKeys();

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

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

// Query keys in the cache that begins with '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

// Delete an item with a specific key
$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 'Cache does not exists!';
}

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

// Setting the cache when getting a result
$robots = $cache->get($cacheKey, 3600);

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

    // Store it in the cache
    $cache->save($cacheKey, $robots);
}

Setting the lifetime when saving:

<?php

$cacheKey = 'my.cache';

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

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

    // Setting the cache when saving data
    $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,
    ]
);

// Backends are registered from the fastest to the slower
$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/',
            ]
        ),
    ]
);

// Save, saves in every backend
$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 它用来缓存任何类型的 PHP 数据没有将其序列化。

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 存储数据到替代 PHP 缓存 (APC)。 APC APC
Phalcon\Cache\Backend\Apcu 存储数据的处理 (APC 不操作码缓存) APCu APCu
Phalcon\Cache\Backend\File 存储到本地普通文件的数据。    
Phalcon\Cache\Backend\Libmemcached 存储到 memcached 服务器数据。 Memcached Memcached
Phalcon\Cache\Backend\Memcache 存储到 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:

选项 描述
前缀 自动预置到缓存键的前缀。
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 用于动态跟踪的缓存键。
前缀 自动预置到缓存键的前缀。
persistent_id 若要创建一个请求之间仍然存在的实例,使用 persistent_id 指定实例的唯一 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;

// Cache data for 2 days
$frontCache = new FrontData(
    [
        'lifetime' => 172800,
    ]
);

// Create the Cache setting memcached connection options
$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:

选项 描述
前缀 自动预置到缓存键的前缀。
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:

选项 描述
前缀 自动预置到缓存键的前缀。

APCU Backend Options

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

选项 描述
前缀 自动预置到缓存键的前缀。

Mongo Backend Options

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

选项 描述
前缀 自动预置到缓存键的前缀。
服务器 MongoDB 的连接字符串。
db 输入数据库名称。
collection Mongo 集合在数据库中。

XCache Backend Options

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

选项 描述
前缀 自动预置到缓存键的前缀。

Redis Backend Options

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

选项 描述
前缀 自动预置到缓存键的前缀。
host Redis的主机。
port Redis的端口
auth 密码保护Redis服务器进行身份验证的密码。
persistent 创建与Redis持久连接。
index Frontend数据库使用的索引。

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