terça-feira, 24 de agosto de 2010

O Cache e o PHP

Eu vejo muita confusão ainda nos dias de hoje sobre o que o cache é e qual o seu papel na aplicação. A idéia desse post é explicar o cache e mostrar algumas implementações simples onde o cache pode fazer grande diferença.

Definindo Cache

Cache, por definição não se restringe ao PHP. Na verdade é uma estratégia de otimização de um processo que é aplicada em uma camada superior do problema que esta sendo resolvido. Por exemplo, vemos cache em processadores armazenando pequenos blocos de memória que tem um volume de requisições grande. Vemos cache nos HDs que desempenham o mesmo papel.

Em suma, cache é um local de acesso mais rápido onde colocamos a informações que usamos mais. O cache também é o local onde colocamos resultados de processos dispendiosos que certamente iremos precisar novamente. Porem o recurso de hardware associado ao cache são geralmente bastante limitados. Por isso temos que escolher com cuidado o que colocar no cache.

Cache e a Web

Esse talvez seja o escopo que causa a maior confusão. Entre o brower e o servidor temos uma quantidade muito grande de "caches diferentes". Temos no navegador cache de imagens, cache de documentos, de DNS. No servidor temos cache do opcode (no caso do PHP), cache de output, de funções, de requisições, de web services.

E, talves, o ponto que cause maior confusão é o fado de que o servidor é capaz de manipular como o navegador deve fazer o cache de cada response. Vamos dividi-las e conquista-las uma a uma.

Cache e o Browser

Então até aqui varias explicações teóricas e a pratica? Tranquilo, aqui podemos ver algo mais interessante e ver como alterar o alterar o cache do browser.

header("Cache-Control: no-cache, must-revalidate");

O header da resposta http é responsavel por indicar o tempo de cache para o browser. Esse código, por exemplo faz com que o browser ignore o cache para esse elemento, requisitando uma nova copia a cada necessidade. Mas lembre-se, cada navegador pode interpretar as instruções de maneira diferente. E que mesmo quando não é definido o header padrão é enviado, oque é o caso de imagens. Se precisar de mais detalhes do que usar no header começe por aqui.

Cache e o Servidor

Aceleradores

Também conhecidos com aceleradores, algumas extensões do PHP são responsáveis por fazer cache do código intermediário gerado durante a execução de um script. Esse código, chamado opcode, pode levar alguns preciosos milésimos de segundos para ser gerado, mas em escala resulta num ganho considerável. Algumas alternativas de aceleradores:

O APC é bastante popular e vc pode notar que ele se encontra no manual do próprio PHP, apesar de ser uma extensão. Se vc quer performance é um tiro certo instala-lo.

Implementando Cache

As demais formas de cache passam por identificar o seu problema e ataca-lo diretamente. Identificar onde colocar o cache parece a parte mais fácil, mas não se engane, essa pode ser a parte mais traiçoeira, fazendo vc gastar recursos desnecessários. É importante identificar onde o cache pode ou não ser aplicado. Por exemplo, vc tem um web app que coloca o nome usuário logado na tela, ou qualquer outra informação especifica por usuário. Nesse caso é inviável vc aplicar um cache completo de output. Essa é analise que tem de ser feita antes de aplicar o cache. Ou seja, essa parte esta lenta? Da pra por cache? Cache nela!

Ok, mas isso é muito papo e pouco cache, digo, pouca ação. Como eu crio o cache de alguma coisa? Existem varias maneiras de criar um cache, desde usar a session a usar um framework pra cuidar do trabalho.

$frontendOptions = array(
   'lifetime' => 7200, // cache lifetime of 2 hours
   'automatic_serialization' => true
);
 
$backendOptions = array(
    // Directory where to put the cache files
    'cache_dir' => './tmp/' 
);
 
// getting a Zend_Cache_Core object
$cache = Zend_Cache::factory('Core',
                             'File',
                             $frontendOptions,
                             $backendOptions);

$id = 'myBigLoop'; // cache id of "what we want to cache"
 
if (!($data = $cache->load($id))) {
    // cache miss
 
    $data = '';
    for ($i = 0; $i < 10000; $i++) {
        $data = $data . $i;
    }
 
    $cache->save($data);
 
}
 
// [...] do something with $data (echo it, pass it on etc.)

Usando esse trecho de código vc pode implementar o cache de qualquer coisa. Métodos, widgets, consultas, o que precisar. E o mais legal de usar o Zend_Cache é que vc pode fazer cache em arquivo, em sqlit, memcache, apc, xcache, entre outros só trocando a variável o parâmetro do factory desse jeito:

$cache = Zend_Cache::factory('Core',
                            'APC',
                             $frontendOptions,
                             $backendOptions);

Mas lembre que os parâmetros do $backendOptions devem acompanhar o backend selecionado. Mais detalhes em Zend_Cache