speed 18. Cache e Performance - Otimização Avançada

Performance é essencial. Usuários abandonam sites que demoram mais de 3 segundos para carregar. Cache é a técnica mais eficaz para acelerar sua aplicação: em vez de processar a mesma requisição várias vezes, você armazena o resultado e retorna instantaneamente.

info
Resultado: Com cache bem implementado, você pode reduzir tempos de resposta de 500ms para 10ms. Isso significa servir 50x mais requisições com o mesmo hardware!

layers Tipos de Cache

storage Database Cache

Cacheia resultados de queries SQL. Ideal para dados que mudam pouco (listagem de cursos).

api API Response Cache

Cacheia respostas completas de endpoints. Perfeito para dados públicos (home, sobre).

web Browser Cache

CSS, JS e imagens são cacheados no navegador. Reduz tráfego e acelera carregamento.

code Backend PHP - Cache Service (Redis)

warning
Instalar Redis: apt-get install redis-server (Linux) ou brew install redis (Mac). Windows: use Docker.
// services/CacheService.php
prefix = $prefix;
        $this->redis = new Redis();
        $this->redis->connect(
            $_ENV['REDIS_HOST'] ?? '127.0.0.1',
            $_ENV['REDIS_PORT'] ?? 6379
        );
        
        if (!empty($_ENV['REDIS_PASSWORD'])) {
            $this->redis->auth($_ENV['REDIS_PASSWORD']);
        }
    }
    
    /**
     * Obter valor do cache
     */
    public function get(string $key) {
        $value = $this->redis->get($this->makeKey($key));
        
        if ($value === false) {
            return null;
        }
        
        return json_decode($value, true);
    }
    
    /**
     * Armazenar no cache
     */
    public function set(string $key, $value, ?int $ttl = null): bool {
        $ttl = $ttl ?? $this->defaultTtl;
        
        return $this->redis->setex(
            $this->makeKey($key),
            $ttl,
            json_encode($value)
        );
    }
    
    /**
     * Deletar do cache
     */
    public function delete(string $key): bool {
        return $this->redis->del($this->makeKey($key)) > 0;
    }
    
    /**
     * Limpar cache por padrão
     */
    public function flush(string $pattern = '*'): void {
        $keys = $this->redis->keys($this->prefix . ':' . $pattern);
        
        if (!empty($keys)) {
            $this->redis->del(...$keys);
        }
    }
    
    /**
     * Remember: busca do cache ou executa callback
     */
    public function remember(string $key, callable $callback, ?int $ttl = null) {
        $value = $this->get($key);
        
        if ($value !== null) {
            return $value;
        }
        
        $value = $callback();
        $this->set($key, $value, $ttl);
        
        return $value;
    }
    
    /**
     * Incrementar contador
     */
    public function increment(string $key, int $by = 1): int {
        return $this->redis->incrBy($this->makeKey($key), $by);
    }
    
    /**
     * Gerar chave com prefixo
     */
    private function makeKey(string $key): string {
        return $this->prefix . ':' . $key;
    }
}

// ===== EXEMPLOS DE USO =====

$cache = new CacheService();

// Cachear lista de cursos
$courses = $cache->remember('courses:all', function() use ($db) {
    $stmt = $db->query("SELECT * FROM courses WHERE status = 'published'");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}, 3600); // Cache por 1 hora

// Quando criar/editar curso, limpar cache
$cache->delete('courses:all');

// Cachear dados de um curso específico
$courseId = 123;
$course = $cache->remember("course:{$courseId}", function() use ($db, $courseId) {
    $stmt = $db->prepare("SELECT * FROM courses WHERE id = ?");
    $stmt->execute([$courseId]);
    return $stmt->fetch(PDO::FETCH_ASSOC);
}, 7200); // 2 horas

optimization Otimização de Queries SQL

// ❌ LENTO - Query sem índice
SELECT * FROM users WHERE email = 'joao@email.com';
// Tempo: 500ms (tabela com 100k registros)

// ✅ RÁPIDO - Adicionar índice
CREATE INDEX idx_email ON users(email);
// Tempo: 5ms


// ❌ LENTO - N+1 Problem
$courses = $db->query("SELECT * FROM courses")->fetchAll();
foreach ($courses as $course) {
    // 1 query por curso!
    $stmt = $db->prepare("SELECT * FROM course_lessons WHERE course_id = ?");
    $stmt->execute([$course['id']]);
    $lessons = $stmt->fetchAll();
}
// Total: 1 + N queries (se 100 cursos = 101 queries)

// ✅ RÁPIDO - Eager Loading com JOIN
$sql = "
    SELECT 
        c.*,
        JSON_ARRAYAGG(
            JSON_OBJECT(
                'id', l.id,
                'title', l.title,
                'duration', l.duration_minutes
            )
        ) as lessons
    FROM courses c
    LEFT JOIN course_lessons l ON c.id = l.course_id
    GROUP BY c.id
";
// Total: 1 query apenas!


// ===== PAGINAÇÃO EFICIENTE =====

// ❌ LENTO - OFFSET alto
SELECT * FROM courses LIMIT 100 OFFSET 50000;
// Banco precisa processar 50.100 linhas

// ✅ RÁPIDO - Cursor-based pagination
SELECT * FROM courses WHERE id > ? LIMIT 100;
// Usa índice primary key


// ===== QUERY OPTIMIZATION TIPS =====

// 1. Use EXPLAIN para analisar queries
EXPLAIN SELECT * FROM courses WHERE category_id = 5;

// 2. Sempre tenha índices em foreign keys
CREATE INDEX idx_category_id ON courses(category_id);

// 3. Evite SELECT * quando possível
SELECT id, title, price FROM courses; // Mais rápido

// 4. Use COUNT(*) com WHERE otimizado
SELECT COUNT(*) FROM courses WHERE status = 'published';
CREATE INDEX idx_status ON courses(status);

// 5. Limite resultados quando possível
SELECT * FROM courses LIMIT 100; // Não retorne milhares de registros

web Frontend React - Cache e Memoization

// React Query - Cache automático de requisições
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

const API_URL = import.meta.env.VITE_API_URL;

// Hook com cache
export const useCourses = () => {
    return useQuery({
        queryKey: ['courses'],
        queryFn: async () => {
            const { data } = await axios.get(`${API_URL}/courses`);
            return data.courses;
        },
        staleTime: 5 * 60 * 1000, // 5 minutos
        cacheTime: 10 * 60 * 1000, // 10 minutos
    });
};

// Mutação com invalidação de cache
export const useCreateCourse = () => {
    const queryClient = useQueryClient();
    
    return useMutation({
        mutationFn: async (courseData) => {
            const { data } = await axios.post(`${API_URL}/courses`, courseData);
            return data;
        },
        onSuccess: () => {
            // Invalida cache para refetch
            queryClient.invalidateQueries(['courses']);
        }
    });
};

// Uso no componente:
const CoursesList = () => {
    const { data: courses, isLoading, error } = useCourses();
    
    if (isLoading) return 
Carregando...
; if (error) return
Erro: {error.message}
; return (
{courses.map(course => ( ))}
); }; // ===== MEMOIZATION - useMemo e useCallback ===== import { useMemo, useCallback } from 'react'; const ExpensiveComponent = ({ items, searchTerm }) => { // Cacheia resultado de computação pesada const filteredItems = useMemo(() => { console.log('Filtrando items...'); return items.filter(item => item.title.toLowerCase().includes(searchTerm.toLowerCase()) ); }, [items, searchTerm]); // Só recalcula se items ou searchTerm mudar // Cacheia função para não recriar a cada render const handleClick = useCallback((id) => { console.log('Clicked:', id); }, []); // Função nunca muda return (
{filteredItems.map(item => (
handleClick(item.id)}> {item.title}
))}
); }; // ===== VIRTUALIZAÇÃO - React Window ===== import { FixedSizeList } from 'react-window'; const VirtualizedList = ({ items }) => { // Renderiza apenas itens visíveis const Row = ({ index, style }) => (
{items[index].title}
); return ( {Row} ); };

check_circle Checklist de Performance

✅ Backend

  • Implementar cache com Redis/Memcached
  • Adicionar índices em colunas pesquisadas
  • Evitar N+1 queries (usar JOINs)
  • Paginar resultados grandes
  • Usar prepared statements (evita parsing)
  • Habilitar OPcache do PHP
  • Comprimir respostas (gzip)

✅ Frontend

  • Usar React Query para cache de API
  • Lazy loading de componentes
  • Code splitting (vite build)
  • Otimizar imagens (WebP, lazy load)
  • useMemo para computações pesadas
  • useCallback para funções estáveis
  • Virtualização para listas grandes
rocket_launch
Próximo Passo: Performance otimizada! Agora vamos implementar testes automatizados para garantir que tudo funciona corretamente.