Fragmentação da memória Na hospedagem web, o PHP-FPM e o MySQL ficam lentos, mesmo que pareça haver RAM suficiente, porque a memória se divide em muitos blocos pequenos e as alocações maiores falham. Mostro na prática como a fragmentação encarece as solicitações, aciona o swap e por que o ajuste direcionado no PHP e no MySQL melhora visivelmente os tempos de carregamento, a confiabilidade e a escalabilidade.
Pontos centrais
- PHP-FPM Reciclar: reiniciar processos regularmente através de pm.max_requests
- Tampão dosear: manter o buffer por ligação MySQL conservador
- Troca Evitar: reduzir a swappiness, ter em conta o NUMA
- Tabelas Manter: verificar Data_free, otimizar de forma direcionada
- Monitorização Aproveitar: identificar tendências antecipadamente e agir
O que significa fragmentação de memória no dia a dia da hospedagem?
No alojamento, encontra Fragmentação em processos de longa duração que solicitam e liberam memória constantemente, criando lacunas no espaço de endereçamento. Embora a soma da RAM livre pareça grande, faltam blocos contíguos para alocações maiores, o que torna as tentativas de alocação mais lentas. Vejo isso em trabalhadores PHP‑FPM e em mysqld, que parecem cada vez mais „inchados“ após algumas horas. O efeito torna cada pedido minimamente mais caro e aumenta significativamente os tempos de resposta sob carga. Isso faz com que picos como promoções de vendas ou backups se tornem um obstáculo, embora a CPU e a rede permaneçam discretas.
Por que o PHP-FPM gera fragmentação
Cada trabalhador PHP‑FPM carrega código, plugins e dados num seu próprio Espaço de endereçamento, atende a uma grande variedade de solicitações e deixa lacunas dispersas ao liberar recursos. Com o tempo, os processos crescem e liberam memória internamente, mas não necessariamente para o sistema operativo, o que aumenta a fragmentação. Diferentes scripts, tarefas de importação e processamento de imagens intensificam essa mistura e levam a padrões de alocação variáveis. Observo isso como um aumento gradual da RAM, embora a carga e o tráfego pareçam constantes. Sem reciclagem, essa fragmentação interna torna a alocação mais lenta e dificulta o planeamento em caso de alto número de visitantes.
Consequências significativas para os tempos de carregamento e a fiabilidade
Processos fragmentados geram mais Despesas gerais na gestão da memória, o que se traduz em backends administrativos mais lentos e checkouts hesitantes. As lojas WordPress ou grandes instâncias CMS, em particular, reagem de forma lenta quando muitas solicitações simultâneas encontram trabalhadores fragmentados. Isso resulta em tempos limite, erros 502/504 e repetidas tentativas no lado NGINX ou Apache. Eu leio essas situações em métricas como picos de tempo de resposta, aumento da linha de base de RAM e aumento repentino do uso de swap. Ignorar isso significa desperdiçar desempenho, piorar a experiência do utilizador e aumentar a taxa de desistência em funis críticos.
Configurar corretamente o PHP-FPM: limites, pools, reciclagem
Aposte em objetivos realistas Limites, pools separados e reciclagem consistente para conter a fragmentação. Eu termino o pm.max_requests de forma que os trabalhadores reiniciem regularmente, sem perturbar os visitantes atuais. Para perfis de tráfego com picos de carga, pm = dynamic costuma ser mais adequado, enquanto pm = ondemand economiza RAM em sites tranquilos. Eu mantenho o memory_limit por site deliberadamente moderado e o ajusto com base em scripts reais; o tópico fornece uma introdução. Limite de memória PHP. Além disso, separo projetos com grande carga em pools próprios, para que um consumidor de memória não prejudique todos os sites.
OPcache, pré-carregamento e alocador PHP em foco
Para reduzir a fragmentação no processo PHP, eu aposto em um dimensionamento adequado. OPcache. Um opcache.memory_consumption generoso, mas não exagerado, e strings internadas suficientes reduzem alocações repetidas por pedido. Observo a taxa de acertos, o desperdício e a capacidade restante; se o desperdício aumentar com o tempo, é melhor planear uma recarga do que deixar os trabalhadores crescerem descontroladamente. O pré-carregamento pode manter o código quente estável na memória e, assim, suavizar os padrões de alocação, desde que a base de código esteja devidamente preparada. Além disso, presto atenção ao Seleção do alocador: Dependendo da distribuição, o PHP‑FPM e as extensões funcionam com diferentes implementações Malloc. Alocadores alternativos, como o jemalloc, reduzem significativamente a fragmentação em algumas configurações. No entanto, só implemento essas alterações depois de testadas, pois a depuração, o perfil DTrace/eBPF e os dumps de memória reagem de forma diferente dependendo do alocador.
Para tarefas que exigem muita memória, como processamento de imagens ou exportações, prefiro pools separados com limites mais restritos. Assim, o pool principal não cresce de forma descontrolada e a fragmentação permanece isolada. Além disso, limito as extensões que consomem muita memória (por exemplo, através de variáveis de ambiente) e utilizo contrapressão: as solicitações que requerem buffers grandes são reduzidas ou transferidas para filas assíncronas, em vez de sobrecarregar todos os trabalhadores ao mesmo tempo.
Entender a memória do MySQL: buffer, ligações, tabelas
No MySQL, eu distingo entre globais Tampão como o buffer pool InnoDB, buffer por ligação e estruturas temporárias, que podem crescer a cada operação. Valores demasiado elevados levam a um aumento exponencial da necessidade de RAM e a uma maior fragmentação ao nível do sistema operativo, em caso de carga de ligação elevada. Além disso, as tabelas ficam fragmentadas devido a atualizações/eliminações e deixam partes de dados livres, o que prejudica a utilização do buffer pool. Por isso, verifico regularmente o tamanho, as taxas de acerto e o número de tabelas temporárias em disco. A seguinte visão geral ajuda-me a identificar sintomas típicos com precisão e a ponderar medidas a tomar.
| Sintoma | Causa provável | Medida |
|---|---|---|
| A RAM aumenta constantemente, o swap começa | Buffer pool demasiado grande ou muitos buffers por ligação | Limitar o pool ao tamanho adequado, reduzir por buffer de conexão |
| Muitas variedades/junções lentas | Índices em falta, buffer sort/join exagerado | Verificar índices, manter sort/join conservador |
| Grande quantidade de dados livres nas tabelas | Atualizações/eliminações significativas, páginas fragmentadas | OPTIMIZE direcionado, arquivamento, simplificação do esquema |
| Picos em tabelas temporárias de disco | tmp_table_size demasiado pequeno ou consultas inadequadas | Aumentar os valores moderadamente, reestruturar as consultas |
Ajuste da memória do MySQL: selecionar tamanhos em vez de exagerar
Eu seleciono o buffer pool InnoDB de forma que o Sistema operativo mantém espaço suficiente para o cache do sistema de ficheiros e serviços, especialmente em servidores combinados com Web e DB. Eu escalo conservativamente o buffer por conexão, como sort_buffer_size, join_buffer_size e read-Buffer, para que muitas conexões simultâneas não causem sobrecarga na RAM. Eu defino tmp_table_size e max_heap_table_size de forma que operações sem importância não exijam tabelas enormes na memória. Para mais ajustes, eu encontro em Desempenho do MySQL Ideias úteis para reflexão. O decisivo continua a ser: prefiro definir algo mais restrito e medir, em vez de aumentar cegamente e arriscar fragmentação e swap.
InnoDB em detalhe: estratégias de reconstrução e instâncias de pool
Para manter o MySQL internamente mais „compacto“, pretendo fazer regularmente Reconstruções para tabelas com grande volume de gravações. Um OPTIMIZE TABLE específico (ou uma reconstrução online por ALTER) reúne dados e índices e reduz Sem dados. Para isso, escolho intervalos de tempo com baixa carga, uma vez que as reconstruções são intensivas em termos de E/S. A opção innodb_file_per_table Eu mantenho ativo, porque permite reconstruções controladas por tabela e reduz o risco de que „elementos problemáticos“ individuais fragmentem todo o ficheiro do espaço de tabela.
Eu uso vários Instancias de buffer pool (innodb_buffer_pool_instances) em relação ao tamanho do pool e aos núcleos da CPU, para aliviar os latches internos e distribuir os acessos. Isso não só melhora a paralelidade, como também suaviza os padrões de alocação no pool. Além disso, verifico o tamanho dos logs de refazer e a atividade dos threads de purga, pois o histórico acumulado pode ocupar memória e E/S, o que aumenta a fragmentação no nível do sistema operativo. É importante: altere as configurações gradualmente, meça e mantenha-as apenas se as latências e as taxas de erro realmente diminuírem.
Evitar swap: configurações do kernel e NUMA
Assim que o Linux começa a trocar ativamente, os tempos de resposta aumentam em ordens de grandeza, porque os acessos à RAM tornam-se lentos em termos de E/S. Reduzo significativamente o vm.swappiness para que o kernel utilize a RAM física por mais tempo. Em hosts com vários processadores, verifico a topologia NUMA e, se necessário, ativo o interleaving para reduzir a utilização desigual da memória. Para obter informações básicas e sobre a influência do hardware, a perspectiva da Arquitetura NUMA. Além disso, planeio reservas de segurança para o cache de páginas, pois um cache esgotado acelera a fragmentação de toda a máquina.
Páginas enormes transparentes, sobrecompromisso e escolha do alocador
Páginas enormes transparentes (THP) podem causar picos de latência em bases de dados, porque a fusão/divisão de páginas grandes ocorre em momentos inoportunos. Eu defino o THP como „madvise“ ou desativo-o quando o MySQL reage com lentidão sob carga. Ao mesmo tempo, observo que Compromisso excessivo: Com uma configuração vm.overcommit_memory demasiado generosa, corre-se o risco de OOM-kills precisamente quando a fragmentação torna raros os blocos contíguos de grande dimensão. Prefiro definições conservadoras de overcommit e verifico regularmente os registos do kernel para detetar sinais de pressão de memória.
O Seleção do alocador vale a pena dar uma olhada no nível do sistema. glibc‑malloc, jemalloc ou tcmalloc comportam-se de maneira diferente em relação à fragmentação. Eu sempre testo alternativas isoladamente, meço o histórico RSS e as latências e só implemento as alterações se as métricas permanecerem estáveis sob tráfego real. A utilidade varia muito dependendo da carga de trabalho, da combinação de extensões e da versão do sistema operativo.
Identificar fragmentação: métricas e dicas
Presto atenção ao aumento gradual linhas de base na RAM, mais respostas 5xx sob carga e atrasos nas ações administrativas. Uma análise das estatísticas PM do PHP-FPM mostra se os filhos atingem os limites ou vivem por muito tempo. No MySQL, verifico as taxas de acertos, tabelas temporárias no disco e Data_free por tabela. Paralelamente, métricas do sistema operativo, como falhas de página, swap-in/out e indicadores de fragmentação de memória, ajudam dependendo da versão do kernel. Quem reunir esses sinais reconhece padrões antecipadamente e pode planejar medidas.
Aprofundar a monitorização: como eu junto os sinais
Eu correlaciono Métricas de aplicação (latências p95/p99, taxas de erro) com métricas de processo (RSS por trabalhador FPM, memória mysqld) e Valores OS (Pagecache, Slab, Major Faults). No PHP‑FPM, utilizo a interface de estado para comprimentos de fila, filhos ativos/gerados e tempo de vida dos trabalhadores. No MySQL, observo Created_tmp_disk_tables, Handler_write/Handler_tmp_write, bem como Buffer‑Pool‑Misses. Além disso, analiso mapas de processos (pmap/smaps) para descobrir se surgiram muitas pequenas arenas. Para mim, é importante a orientação para as tendências: não é o pico isolado, mas sim a mudança gradual ao longo de horas/dias que determina se a fragmentação se torna um perigo real.
Rotina prática: manutenção e atualização de dados
Eu arrumo regularmente Dados em: sessões expiradas, registos antigos, revisões desnecessárias e caches órfãos. Para tabelas altamente mutáveis, planeio janelas OPTIMIZE específicas para consolidar páginas fragmentadas. Distribuo grandes tarefas de importação ou ondas cron ao longo do tempo, para que nem todos os processos solicitem buffers máximos ao mesmo tempo. Em projetos em crescimento, separo a web e o banco de dados antecipadamente, para isolar padrões que consomem muita memória. Essa disciplina mantém a memória de trabalho mais coesa e reduz o risco de latências de burst.
Calcular tamanhos com precisão: dimensionar limites e pools
Eu determino pm.max_children com base na RAM realmente disponível para PHP. Para isso, eu meço o RSS médio de um trabalhador sob carga real (incluindo extensões e OPcache) e adiciono margens de segurança para picos. Exemplo: num host de 16 GB, reservo 4-6 GB para o sistema operativo, cache de página e MySQL. Restam 10 GB para PHP; com 150 MB por trabalhador, isso resulta teoricamente em 66 filhos. Na prática, defino pm.max_children para ~80-90% deste valor, para deixar margem para picos, ou seja, cerca de 52-58. pm.max_requests Eu seleciono de forma que os trabalhadores sejam reciclados antes de uma fragmentação perceptível (frequentemente na faixa de 500 a 2.000, dependendo da combinação de códigos).
Para o MySQL, calculo o Grupo de tampões a partir do tamanho dos dados ativos, não do tamanho total da base de dados. Tabelas e índices importantes devem caber nela, mas o cache do sistema operativo precisa de espaço para binlogs, sockets e ativos estáticos. Por buffer de conexão, calculo com a paralelidade máxima realista. Se forem possíveis 200 conexões, não dimensiono de forma que, no total, vários gigabytes por conexão explodam, mas defino limites rígidos que não representam risco de swap, mesmo em picos.
Desacoplar filas, processamento de imagens e tarefas secundárias
Muitos problemas de fragmentação surgem quando trabalhos temporários os mesmos pools que as solicitações front-end. Para exportações, rastreamentos, conversões de imagens ou atualizações de índices de pesquisa, utilizo pools FPM separados ou tarefas CLI com limite máximoLimites. Limito adicionalmente as operações de imagem com GD/Imagick através de limites de recursos adequados, para que conversões individuais de grande dimensão não „fragmentem“ todo o espaço de endereçamento. Planeio as tarefas de forma escalonada e atribuo-lhes limites de concorrência próprios, para que não sobrecarreguem o caminho do frontend.
Contentores e virtualização: Cgroups, OOM e efeitos balão
Nos contentores, observo Limites de memória e o OOM Killer com especial atenção. A fragmentação faz com que os processos sejam executados mais cedo nos limites do Cgroup, embora ainda haja RAM livre no host. Eu defino o pm.max_children estritamente de acordo com o limite do contentor e mantenho reserva suficiente para amortecer picos. Evito a troca dentro dos contentores (ou no host), porque a indireção adicional aumenta os picos de latência. Nas VMs, verifico o ballooning e o KSM/UKSM; a deduplicação agressiva economiza RAM, mas pode causar latência adicional e distorcer o quadro de fragmentação.
Lista de verificação curta sem pontos-chave
Primeiro, defino um objetivo realista. memory_limit por site e observo como se comporta o pico de utilização ao longo dos dias. Em seguida, ajusto o PHP-FPM com valores pm adequados e um pm.max_requests razoável, para que os trabalhadores fragmentados funcionem conforme o planeado. Para o MySQL, concentro-me num tamanho adequado do buffer pool e num buffer por conexão conservador, em vez de aumentos generalizados. No lado do kernel, reduzo o swappiness, verifico as configurações NUMA e mantenho reservas para o cache de página. Por fim, avalio tabelas com anomalias Data_free e planeio otimizações fora do dia a dia.
Resumindo: o que importa na empresa
O maior efeito contra a fragmentação da memória é alcançado com uma abordagem consistente. Reciclagem o PHP‑FPM‑Worker, limites moderados e pools limpos. O MySQL beneficia de tamanhos razoáveis para o buffer pool e o buffer por ligação, bem como de tabelas organizadas. Evito proativamente o swap, prestando atenção ao swappiness e ao NUMA e reservando RAM livre para o cache de ficheiros. A monitorização revela padrões insidiosos antes que os utilizadores se apercebam e permite intervenções tranquilas e planeadas. Quem utiliza estas alavancas de forma disciplinada mantém o PHP e o MySQL mais rápidos, fiáveis e económicos, sem necessidade de atualizações imediatas de hardware.


