Cache de Dados

O Cache de Dados é responsável por armazenar uma ou mais variáveis PHP em um arquivo temporário para ser recuperado posteriormente. Este também é a fundação para funcionalidades mais avançadas do cache, como cache de consulta e cache de página.

O código a seguir é um padrão de uso típico de cache de dados, onde $cache refere-se a um Componente de Cache:

// tentar recuperar $data do cache
$data = $cache->get($key);

if ($data === false) {

    // $data não foi encontrado no cache, calculá-la do zero

    // armazenar $data no cache para que esta possa ser recuperada na próxima vez
    $cache->set($key, $data);
}

// $data é acessível a partir daqui

Componentes de Cache

O cache de dados se baseia nos, então chamados, Componentes de Cache que representam vários armazenamentos de cache, como memória, arquivos, bancos de dados.

Componentes de Cache são normalmente registrados como componentes de aplicação para que possam ser globalmente configuráveis e acessíveis. O código a seguir exibe como configurar o componente de aplicação cache para usar memcached com dois servidores de cache:

'components' => [
    'cache' => [
        'class' => 'yii\caching\MemCache',
        'servers' => [
            [
                'host' => 'servidor1',
                'port' => 11211,
                'weight' => 100,
            ],
            [
                'host' => 'servidor2',
                'port' => 11211,
                'weight' => 50,
            ],
        ],
    ],
],

Você pode então, acessar o componente de cache acima usando a expressão Yii::$app->cache.

Já que todos os componentes de cache suportam as mesmas APIs, você pode trocar o componente de cache por outro reconfigurando-o nas configurações da aplicação sem modificar o código que usa o cache. Por exemplo, você pode modificar a configuração acima para usar APC cache:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
    ],
],

Dica: Você pode registrar múltiplos componentes de cache na aplicação. O componente chamado cache é usado por padrão por muitas classes dependentes de cache (ex., yii\web\UrlManager).

Sistemas de Cache Suportados

Yii suporta uma ampla gama de sistemas de cache. A seguir um resumo:

  • yii\caching\ApcCache: usa a extensão do PHP APC. Esta opção pode ser considerada a mais rápida ao se implementar o cache de uma aplicação densa e centralizada (por exemplo, um servidor, sem balanceadores de carga dedicados, etc.).
  • yii\caching\DbCache: usa uma tabela no banco de dados para armazenar os dados em cache. Para usar este cache você deve criar uma tabela como especificada em yii\caching\DbCache::$cacheTable.
  • yii\caching\DummyCache: serve apenas como um substituto e não faz nenhum cache na realidade. O propósito deste componente é simplificar o código que precisa checar se o cache está disponível. Por exemplo, durante o desenvolvimento, se o servidor não suporta cache, você pode configurar um componente de cache para usar este cache. Quando o suporte ao cache for habilitado, você pode trocá-lo para o componente correspondente. Em ambos os casos, você pode usar o mesmo código Yii::$app->cache->get($key) para tentar recuperar os dados do cache sem se procupar que Yii::$app->cache possa ser null.
  • yii\caching\FileCache: usa arquivos para armazenar os dados em cache. Este é particularmente indicado para armazenar grandes quantidades de dados como o conteúdo da página.
  • yii\caching\MemCache: usa o memcache do PHP e as extensões memcached. Esta opção pode ser considerada a mais rápida ao se implementar o cache em aplicações distribuídas (ex., vários servidores, balanceadores de carga, etc.)
  • yii\redis\Cache: implementa um componente de cache baseado em armazenamento chave-valor Redis (requer redis versão 2.6.12 ou mais recente).
  • yii\caching\WinCache: usa a extensão PHP WinCache (veja também).
  • yii\caching\XCache (deprecated): usa a extensão PHP XCache.
  • yii\caching\ZendDataCache (deprecated): usa Cache de Dados Zend como o meio de cache subjacente.

Dica: Você pode usar vários tipos de cache na mesma aplicação. Uma estratégia comum é usar caches baseados em memória para armazenar dados pequenos mas constantemente usados (ex., dados estatísticos), e usar caches baseados em arquivo ou banco da dados para armazenar dados que são maiores mas são menos usados (ex., conteúdo da página).

APIs De Cache

Todos os componentes de caches estendem a mesma classe base yii\caching\Cache e assim suportam as seguintes APIs:

  • get(): recupera um registro no cache usando uma chave específica. Retorna false caso o item não for encontrado no cache ou se o registro está expirado/invalidado.
  • set(): armazena um registro no cache identificado por uma chave.
  • add(): armazena um registro no cache identificado por uma chave se a chave não for encontrada em cache.
  • mget(): recupera múltiplos registros do cache com as chaves especificadas.
  • mset(): armazena múltiplos registros no cache. Cada item identificado por uma chave.
  • madd(): armazena múltiplos registros no cache. Cada item identificado por uma chave. Se a chave já existir em cache, o registro é ignorado.
  • exists(): retorna se a chave específica é encontrada no cache.
  • delete(): remove um registro do cache identificado por uma chave.
  • flush(): remove todos os registros do cache.

Observação: Não armazene o valor boleano false diretamente, porque o método get() retorna falsepara indicar que o registro não foi encontrado em cache. Você pode armazena false em um array e armazenar este em cache para evitar este problema.

Alguns tipos de cache como MemCache, APC, suportam recuperar em lote múltiplos registros em cache, o que poder reduzir o custo de processamento envolvido ao recuperar informações em cache. As APIs mget() e madd() são equipadas para explorar esta funcionalidade. Em caso do cache em questão não suportar esta funcionalidade, ele será simulado.

Como yii\caching\Cache implementa ArrayAccess, um componente de cache pode ser usado como um array. A seguir alguns exemplos:

$cache['var1'] = $valor1;  // equivalente a: $cache->set('var1', $valor1);
$valor2 = $cache['var2'];  // equivalente a: $valor2 = $cache->get('var2');

Chaves de Cache

Cada registro armazenado no cache é identificado por uma chave única. Quando você armazena um registro em cache, você deve especificar uma chave para ele. Mais tarde, quando você quiser recuperar o registro do cache, você deve fornecer a chave correspondente.

Você pode usar uma string ou um valor arbitrário como uma chave do cache. Quando a chave não for uma string, ela será automaticamente serializada em uma string.

Uma estratégia comum ao definir uma chave de cache é incluir todos os fatores determinantes na forma de um array. Por exemplo, yii\db\Schema usa a seguinte chave para armazenar a informação de um esquema de uma tabela do banco de dados.

[
    __CLASS__,              // nome da classe do esquema
    $this->db->dsn,         // nome da fonte de dados da conexão BD
    $this->db->username,    // usuario da conexão BD
    $name,                  // nome da tabela
];

Como você pode ver, a chave inclui toda a informação necessária para especificar unicamente uma tabela do banco.

Quando o cache de diferentes aplicações é armazenado no mesmo lugar, é aconselhável especificar, para cada aplicação, um prefixo único a chave do cache para evitar conflitos entre elas. Isto pode ser feito ao configurar a propriedade yii\caching\Cache::$keyPrefix. Por exemplo, na configuração da aplicação você pode escrever o seguinte código:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
        'keyPrefix' => 'minhaapp',       // um prefíxo de chave único
    ],
],

Para assegurar interoperabilidade, apenas caracteres alfanuméricos devem ser usados.

Expiração de Cache

Um registro armazenado em cache não será apagado a menos que seja removido por alguma política aplicada (por exemplo, espaço determinado para o cache esteja cheio e os registros mais antigos sejam removidos). Para alterar estes comportamento, você pode fornecer um parâmetro de expiração ao chamar set() para armazenar um registro. O parâmetro indica por quantos segundos um registro pode permanecer validado no cache. Quando você chamar get() para recuperar um registro, se o tempo de expiração houver passado, o método retornará false, indicando que o registro não foi encontrado no cache. Por exemplo,

// Manter o registro em cache por até 45 segundos
$cache->set($chave, $registro, 45);

sleep(50);

$data = $cache->get($chave);
if ($registro === false) {
    // $registro está expirado ou não foi encontrado no sistema
}

Dependências de Cache

Além da definição de expiração, um registro em cache pode também ser invalidado por mudanças nas, então chamadas, dependências de cache. Por exemplo, yii\caching\FileDependency representa a dependência na data de modificação de um arquivo. Quando esta dependência muda, significa que o arquivo correspondente foi mudado. Como um resultado, qualquer arquivo com data ultrapassada encontrado no cache deve ser invalidado e a chamada de get() retornará false.

Dependências de Cache são representadas como objetos de classes dependentes de yii\caching\Dependency. Quando você chamar set() para armazenar um registro em cache, você pode passar um objeto de dependência. Por exemplo,

// Criar uma dependência sobre a data de modificação do arquivo exemplo.txt.
$dependencia = new \yii\caching\FileDependency(['fileName' => 'exemplo.txt']);

// O registro expirará em 30 segundos.
// Ele também pode ser invalidado antes, caso o exemplo.txt seja modificado.
$cache->set($key, $data, 30, $dependency);

// O cache verificará se o registro expirou.
// E também verificará se a dependência associada foi alterada.
// Ele retornará false se qualquer uma dessas condições seja atingida.
$data = $cache->get($key);

Abaixo um sumário das dependências de cache disponíveis:

Cache de Consulta

Cache de consulta é uma funcionalidade especial de cache construída com o cache de dados. Ela é fornecida para armazenar em cache consultas ao banco de dados.

O cache de consulta requer uma conexão ao banco de dados e um componente de aplicação de cache válido. A seguir uma utilização básica do cache de consulta, assumindo que $bd é uma instância de yii\db\Connection:

$resultado = $bd->cache(function ($bd) {
  
    // O resultado da consulta SQL será entregue pelo cache
    // se o cache de consulta estiver sido habilitado e o resultado da consulta for encontrado em cache
    return $bd->createCommand('SELECT * FROM clientes WHERE id=1')->queryOne();

});

Cache de consulta pode ser usado pelo DAO da mesma forma que um ActiveRecord:

$resultado = Cliente::getDb()->cache(function ($bd) {
    return Cliente::find()->where(['id' => 1])->one();
});

Informação: Alguns SGBDs (ex., MySQL) também suportam o cache de consulta no servidor. Você pode escolher usá-lo ao invés do mecanismo de cache de consulta. O cache de consulta descrito acima tem a vantagem de poder especificar dependências de cache flexíveis e assim sendo potencialmente mais eficiente.

Configurações

Cache de consulta tem três opções configuráveis globalmente através de yii\db\Connection:

  • enableQueryCache: Configura se o cache de consulta está habilitado. O padrão é true. Observe que para ter efetivamente o cache de consulta habilitado, você também deve ter um cache válido como especificado por queryCache.
  • queryCacheDuration: representa o número de segundos que o resultado de uma
    consulta pode se manter válido em cache. Você pode usar 0 para indicar que o resultado da consulta deve permanecer no cache indefinidamente. Este é o valor padrão usado quando yii\db\Connection::cache() é chamado sem nenhuma especificação de duração.
  • queryCache: representa a ID do componente de aplicação de cache. Seu padrão é 'cache'. Cache de consulta é habilitado apenas se houver um componente de aplicacão de cache válido.

Usando o Cache de Consulta

Você pode usar yii\db\Connection::cache() se tiver múltiplas consultas SQL que precisam ser armazenadas no cache de consulta. Utilize da seguinte maneira,

$duracao = 60;     // armazenar os resultados em cache por 60 segundos
$dependencia = ...;  // alguma dependência opcional

$result = $db->cache(function ($db) {

    // ... executar consultas SQL aqui ...

    return $result;

}, $duracao, $dependencia);

Qualquer consulta SQL na função anônima será armazenada em cache pela duração especificada com a dependência informada. Se o resultado da consulta for encontrado em cache e for válido, a consulta não será necessária e o resultado será entregue pelo cache. Se você não especificar o parâmetro $duracao, o valor de queryCacheDuration será usado.

Ocasionalmente em cache(), você pode precisar desabilitar o cache de consulta para algumas consultas em particular. Você pode usar yii\db\Connection::noCache() neste caso.

$result = $db->cache(function ($db) {

    // consultas SQL que usarão o cache de consulta

    $db->noCache(function ($db) {

        // consultas SQL que não usarão o cache de consulta

    });

    // ...

    return $result;
});

Se você apenas deseja usar o cache de consulta para apenas uma consulta, você pode chamar yii\db\Command::cache() ao construir o comando. Por exemplo,

// usar cache de consulta e definir duração do cache para 60 segundos
$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();

Você pode também usar yii\db\Command::noCache() para desabilitar o cache de consulta para um único comando, Por exemplo,

$result = $db->cache(function ($db) {

    // consultas SQL que usam o cache de consulta

    // não usar cache de consulta para este comando
    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();

    // ...

    return $result;
});

Limitações

O cache de consulta não funciona com resultados de consulta que contêm manipuladores de recursos (resource handlers). Por exemplo, ao usar o tipo de coluna BLOB em alguns SGBDs, o resultado da consulta retornará um manipulador de recurso (resource handler) para o registro na coluna.

Alguns armazenamentos em cache têm limitações de tamanho. Por exemplo, memcache limita o uso máximo de espaço de 1MB para cada registro. Então, se o tamanho do resultado de uma consulta exceder este limite, o cache falhará.