HOME ARQUIVO LIVRO DE VISITAS SITE NORMAL
Há uns tempos olhei para um site pequeno em alojamento partilhado onde uma página demorava cerca de 400 milissegundos até aparecer algo no ecrã. Nada no código era lento e a base de dados estava bem. O PHP estava a ler os mesmos vinte ficheiros do disco em cada pedido, a analisá-los, a compilá-los em opcodes e a deitar tudo fora quando o pedido terminava. Os ficheiros não tinham mudado entre pedidos, por isso o trabalho era igual de cada vez. O OPcache existe para parar isso.
O PHP não executa o texto dos teus ficheiros. Lê um ficheiro, analisa-o e compila-o em opcodes, que são as instruções que o motor corre. Sem cache, isso acontece em cada pedido, para cada ficheiro carregado, incluindo todos os pacotes do Composer em vendor.
O OPcache guarda os opcodes compilados em memória partilhada. O pedido seguinte que precise do mesmo ficheiro recebe a versão compilada diretamente da memória. O ficheiro em disco continua a ser a fonte de verdade, só é lido com menos frequência.
É uma cache de bytecode e nada mais. Não guarda resultados de queries, sessões nem configuração.
No PHP 8.5 a extensão vem compilada no binário, por isso não há nada a instalar. Em versões mais antigas carrega-se explicitamente com uma linha como zend_extension=opcache.so, e a maioria dos painéis de alojamento partilhado tem um interruptor. As diretivas que interessam:
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.enable liga-o no servidor web. Existe uma separada, opcache.enable_cli, para scripts de linha de comandos, normalmente desligada porque um script que corre uma vez não ganha nada com ela.opcache.memory_consumption é quanta memória partilhada a cache pode usar, em megabytes. Quando acaba, o OPcache não falha com estrondo: descarta entradas e reinicia. Um valor demasiado baixo aparece como reinícios no estado, não como erro.opcache.max_accelerated_files é o número de ficheiros que consegue guardar ao mesmo tempo. Define-o acima do número real de ficheiros PHP do teu projeto, senão os que não cabem são recompilados em cada pedido e tudo parece bem enquanto não está.opcache.validate_timestamps decide se o OPcache verifica a data de modificação do ficheiro antes de usar a versão em cache. É esta que muda a forma como fazes deploy.opcache.revalidate_freq é quantos segundos passam entre essas verificações. Com um valor de dois, uma alteração que gravaste há um segundo pode não aparecer até à verificação seguinte.Com validate_timestamps=1 o PHP compara o timestamp do ficheiro em disco com o que ficou registado junto dos opcodes em cache. Se for diferente, recompila. É o que queres na tua máquina: gravar o ficheiro, atualizar a página, ver a alteração.
Em produção é o contrário. Cada uma dessas verificações é uma chamada ao sistema de ficheiros e, com alguns milhares de ficheiros, é tempo gasto em cada pedido sem benefício. O valor habitual em produção é validate_timestamps=0, que diz ao OPcache para confiar no que tem. A consequência é que um deploy não tem efeito até a cache ser limpa.
Por isso a ordem do deploy é: colocar os ficheiros no sítio, limpar a cache. Reiniciar o PHP-FPM faz isso, porque os workers partilham um OPcache e o reinício dá-lhes um novo. Um pequeno script que chama opcache_reset() também serve, desde que o peças por HTTP. Correr esse script na linha de comandos não serve, porque o CLI tem uma cache própria e separada.
<?php
// deploy/reset-opcache.php, apaga depois do primeiro pedido ou protege-o
opcache_reset();
echo 'limpo';
Se fizeres isto com um ficheiro na raiz pública, protege-o ou apaga-o logo a seguir, porque quem o encontrar pode limpar a tua cache.
Na consola, php -i | grep -i opcache mostra se a extensão está carregada, mas reflete a configuração da linha de comandos e não a da web. O que interessa é o SAPI web, e é mais direto perguntar-lhe:
<?php
$status = opcache_get_status(false);
if ($status === false) {
echo 'O OPcache não está ligado neste pedido';
exit;
}
$stats = $status['opcache_statistics'];
echo 'scripts em cache: ', $stats['num_cached_scripts'], PHP_EOL;
echo 'taxa de acerto: ', round($stats['hit_rate'], 1), '%', PHP_EOL;
echo 'memória usada: ', round($status['memory_usage']['used_memory'] / 1048576, 1), ' MB', PHP_EOL;
echo 'reinícios: ', $stats['oom_restarts'], ' por memória, ', $stats['hash_restarts'], ' por hash', PHP_EOL;
Ao fim de alguns pedidos a taxa de acerto deve estar perto dos 100 por cento. Reinícios acima de zero a subir significam que a memória ou os lugares de ficheiro são poucos.
Num projeto de qualquer tamanho começo com 256 MB de memória, um limite de ficheiros confortavelmente acima do número real e timestamps desligados em produção, com um reset explícito no script de deploy. Na minha máquina deixo os timestamps ligados com frequência zero, para que cada gravação seja apanhada de imediato. Esta combinação tem estado certa todas as vezes que a usei.
[ « Minecraft no Elden Ring: como a IA está a juntar jogos ] [ entrada seguinte » ]
*** *** *** ***
Estás a ser o visitante número: 000314 [ Actualizar ]
🎵 A tocar agora: eyes_on_me.mid |||| (toca sozinho quando abres o site!)