Opcache: cache de bytecode
1. O que é o Opcache e por que ele é importante?
O Opcache é uma extensão do PHP que implementa cache de bytecode. Para entender sua importância, precisamos primeiro compreender como o PHP executa scripts tradicionalmente.
Quando um script PHP é executado sem Opcache, o interpretador realiza três etapas a cada requisição:
- Parsing (análise léxica e sintática): o código-fonte é lido do disco e convertido em tokens
- Compilação: os tokens são transformados em opcodes (bytecode)
- Execução: os opcodes são executados pela engine Zend
Esse processo consome recursos significativos de CPU e I/O de disco. O Opcache elimina as duas primeiras etapas após a primeira execução, armazenando o bytecode compilado em memória compartilhada. O resultado é uma redução drástica no tempo de resposta e no consumo de CPU, especialmente em aplicações com muitos arquivos PHP.
// Exemplo: medindo o impacto do Opcache
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
include 'arquivo_pesado.php';
}
$end = microtime(true);
echo "Tempo sem Opcache: " . ($end - $start) . " segundos";
2. Como o Opcache funciona internamente
Ciclo de vida com Opcache
Quando o Opcache está ativo, o fluxo de execução muda:
- O PHP verifica se o bytecode do script já está em cache
- Se estiver presente e válido, carrega diretamente da memória compartilhada
- Se não estiver, compila o script e armazena o resultado
Armazenamento em memória compartilhada (SHM)
O Opcache utiliza memória compartilhada (SHM) para armazenar:
- Bytecode compilado
- Metadados dos arquivos (timestamps, checksums)
- Tabelas de hash para busca rápida
Mecanismo de invalidação
O Opcache usa dois mecanismos principais para invalidar cache:
- Timestamps: verifica a data de modificação do arquivo
- Checksums: calcula um hash do conteúdo do arquivo (mais seguro, mas mais lento)
// Exemplo: verificando status do cache
$status = opcache_get_status();
if ($status && isset($status['opcache_statistics'])) {
echo "Cache hits: " . $status['opcache_statistics']['hits'] . "\n";
echo "Cache misses: " . $status['opcache_statistics']['misses'] . "\n";
}
3. Configuração básica do Opcache no php.ini
As diretivas mais importantes para configurar o Opcache são:
; Ativar Opcache
opcache.enable=1
; Memória total disponível para cache (em megabytes)
opcache.memory_consumption=128
; Número máximo de arquivos em cache
opcache.max_accelerated_files=4000
; Verificar timestamps dos arquivos
opcache.validate_timestamps=1
; Intervalo de revalidação (em segundos)
opcache.revalidate_freq=2
; Buffer de strings internas
opcache.interned_strings_buffer=8
; Máximo de memória desperdiçada antes de reiniciar
opcache.max_wasted_percentage=5
Configuração para produção vs. desenvolvimento
Produção:
opcache.validate_timestamps=0
opcache.revalidate_freq=0
Desenvolvimento:
opcache.validate_timestamps=1
opcache.revalidate_freq=0
4. Otimizando o uso de memória do Opcache
Calculando o tamanho ideal
O tamanho ideal de opcache.memory_consumption depende do tamanho total dos seus scripts PHP. Uma regra prática:
// Script para estimar o tamanho total dos scripts
$total_size = 0;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/caminho/para/seu/projeto')
);
foreach ($iterator as $file) {
if ($file->getExtension() === 'php') {
$total_size += $file->getSize();
}
}
echo "Tamanho total dos arquivos PHP: " . round($total_size / 1024 / 1024, 2) . " MB";
Monitoramento de fragmentação
// Verificando uso de memória e fragmentação
$status = opcache_get_status(false);
if ($status) {
$memory = $status['memory_usage'];
$used_memory = $memory['used_memory'];
$free_memory = $memory['free_memory'];
$wasted_memory = $memory['wasted_memory'];
$wasted_percentage = ($wasted_memory / ($used_memory + $free_memory + $wasted_memory)) * 100;
echo "Memória usada: " . round($used_memory / 1024 / 1024, 2) . " MB\n";
echo "Memória livre: " . round($free_memory / 1024 / 1024, 2) . " MB\n";
echo "Memória desperdiçada: " . round($wasted_percentage, 2) . "%\n";
}
5. Estratégias de invalidação e recarga de cache
Em deploys
// Script de deploy: limpar cache do Opcache
function clear_opcache() {
if (function_exists('opcache_reset')) {
$result = opcache_reset();
if ($result) {
echo "Cache do Opcache limpo com sucesso!\n";
} else {
echo "Falha ao limpar cache do Opcache.\n";
}
}
}
// Invalidar arquivo específico
function invalidate_file($file_path) {
if (function_exists('opcache_invalidate')) {
if (opcache_invalidate($file_path, true)) {
echo "Arquivo $file_path invalidado.\n";
}
}
}
Proteção contra arquivos parciais
; Esperar 2 segundos antes de cachear arquivos recém-modificados
opcache.file_update_protection=2
6. Opcache em ambientes de desenvolvimento
Configuração ideal para desenvolvimento
; Desabilitar cache (ou usar revalidação frequente)
opcache.enable=1
opcache.validate_timestamps=1
opcache.revalidate_freq=0
; Habilitar logs de cache
opcache.log_verbosity_level=1
; Não usar cache de arquivos em disco
opcache.file_cache=0
Ferramentas complementares
Para desenvolvimento, considere usar:
- Symfony VarDumper: fornece informações detalhadas sobre o Opcache
- Laravel Debugbar: exibe estatísticas do Opcache no painel de debug
7. Monitoramento e depuração do Opcache
Funções nativas de monitoramento
// Informações completas de configuração
$config = opcache_get_configuration();
echo "Versão do Opcache: " . $config['version']['version'] . "\n";
echo "Opcache habilitado: " . ($config['directives']['opcache.enable'] ? 'Sim' : 'Não') . "\n";
// Estatísticas detalhadas
$status = opcache_get_status(true);
if ($status) {
$stats = $status['opcache_statistics'];
echo "Taxa de acerto: " . round(
($stats['hits'] / ($stats['hits'] + $stats['misses'])) * 100, 2
) . "%\n";
echo "Arquivos em cache: " . $stats['num_cached_scripts'] . "\n";
echo "Cache cheio: " . ($stats['oom_restarts'] > 0 ? 'Sim' : 'Não') . "\n";
}
Identificando problemas comuns
// Verificar se o cache está cheio
$status = opcache_get_status();
if ($status['opcache_statistics']['oom_restarts'] > 0) {
echo "ALERTA: O cache do Opcache está ficando sem memória!\n";
echo "Considere aumentar opcache.memory_consumption\n";
}
// Verificar arquivos não cacheados
$cached_files = $status['scripts'] ?? [];
echo "Total de arquivos cacheados: " . count($cached_files) . "\n";
8. Boas práticas e armadilhas comuns
Cuidados com arquivos dinâmicos
// Evite isso - arquivos gerados dinamicamente podem causar problemas
$dynamic_file = '/tmp/cache_' . time() . '.php';
file_put_contents($dynamic_file, '<?php return ' . time() . ';');
// Prefira isso - use cache em memória
$cache_key = 'dynamic_data_' . date('Y-m-d');
$data = apcu_fetch($cache_key);
if ($data === false) {
$data = generateExpensiveData();
apcu_store($cache_key, $data, 3600);
}
Cache em disco (file_cache)
; Habilitar cache em disco como fallback
opcache.file_cache=/tmp/opcache
opcache.file_cache_only=0
Limitações importantes
- Ambientes compartilhados: Opcache não funciona bem em hosts compartilhados
- CLI: Por padrão, Opcache está desabilitado na CLI
- Includes dinâmicos:
include $variavelpode não ser cacheado
; Habilitar Opcache para CLI (não recomendado em produção)
opcache.enable_cli=1
Referências
- Documentação Oficial do Opcache (PHP.net) — Documentação completa da extensão Opcache, incluindo todas as diretivas de configuração e funções nativas.
- Opcache Configuration Guide (Kinsta) — Guia prático de configuração e otimização do Opcache para ambientes de produção.
- Understanding Opcache Internals (Nikic's Blog) — Artigo técnico detalhado sobre o funcionamento interno do Opcache, escrito pelo desenvolvedor principal do PHP.
- PHP Opcache Performance Tuning (DigitalOcean) — Tutorial passo a passo para otimizar o desempenho do PHP com Opcache em servidores Linux.
- Opcache Monitoring with opcache_get_status() (SitePoint) — Guia completo sobre como monitorar e depurar o Opcache usando funções nativas do PHP.
- PHP Opcache Best Practices (Laravel News) — Boas práticas e configurações recomendadas para usar Opcache em aplicações Laravel e PHP modernas.
- Opcache File Cache Feature (PHP Watch) — Análise detalhada do recurso de cache em disco do Opcache e quando utilizá-lo.