OpenZFS 2.4: O fim da taxa de memória dupla com Direct I/O

      Roberto Uchoa 9 min de leitura
      OpenZFS 2.4: O fim da taxa de memória dupla com Direct I/O

      Análise técnica do suporte a Direct I/O (O_DIRECT) no OpenZFS 2.4. Descubra como eliminar o buffer duplo em bancos de dados e recuperar sua RAM.

      Compartilhar:

      Você comprou aquele servidor novo, abarrotado de NVMe e com meio terabyte de RAM, achando que finalmente o banco de dados pararia de reclamar de I/O wait. Duas semanas depois, o Grafana mostra a memória topada, o OOM Killer está rondando o processo do PostgreSQL como um urubu faminto e você está explicando para o gerente que "memória livre é memória desperdiçada".

      A verdade é que, se você roda bancos de dados pesados sobre ZFS, você tem pago uma "taxa de estupidez" arquitetural há anos. Não por culpa sua, mas porque o sistema de arquivos insistia em achar que sabia gerenciar cache melhor que o seu banco de dados.

      Com a chegada do suporte robusto a Direct I/O no OpenZFS 2.4, a festa da redundância acabou. Vamos falar sobre como parar de queimar dinheiro com pentes de memória DDR5 servindo apenas para duplicar dados que já existem.

      Resumo em 30 segundos

      • O Problema: Bancos de dados (PostgreSQL/MySQL) e o ZFS (ARC) tentam, ambos, fazer cache dos mesmos dados na RAM, criando uma "cópia dupla" inútil.
      • A Gambiarra Antiga: Limitar o zfs_arc_max manualmente é um curativo que desperdiça ciclos de CPU e exige ajuste constante.
      • A Solução: O OpenZFS 2.4 finalmente estabiliza o suporte a Direct I/O (O_DIRECT), permitindo que aplicações pulem o ARC e falem diretamente com o disco, liberando RAM para o que importa.

      O mistério da RAM desaparecida em servidores de banco de dados

      Se você já administrou um servidor Linux rodando ZFS, conhece a cena: o comando free -h mostra 98% da memória usada, mas os processos de usuário consomem apenas 40%. O resto? Sumiu no buraco negro chamado ARC (Adaptive Replacement Cache).

      Diferente do "page cache" padrão do Linux, que o kernel descarta alegremente quando precisa de RAM, o ARC do ZFS é territorialista. Ele luta para manter os dados cacheados porque, teoricamente, o algoritmo dele (MFU/MRU) é superior. E geralmente é. Exceto quando você roda uma aplicação que também tem seu próprio gerenciamento de memória sofisticado, como o PostgreSQL, Oracle ou MySQL (InnoDB).

      Nesse cenário, o ZFS puxa o bloco do disco para o ARC. O banco de dados pede esse bloco, o kernel copia do ARC para o buffer da aplicação. Resultado: você tem o mesmo bloco de 8KB ocupando espaço em dois lugares distintos da RAM. É o equivalente digital de imprimir um e-mail para ler o papel enquanto olha para a tela.

      A redundância ineficiente entre o ZFS ARC e o buffer pool do PostgreSQL

      Vamos ser técnicos e pragmáticos. O caminho do I/O sem Direct I/O é uma burocracia estatal.

      Quando o Postgres solicita uma leitura:

      1. O ZFS lê o dado do vdev (disco).

      2. O ZFS aloca memória no kernel space e armazena o dado no ARC.

      3. O dado é copiado (via memcpy, queimando ciclos de CPU) do kernel space para o user space.

      4. O Postgres armazena esse dado no seu shared_buffers.

      ⚠️ Perigo: Essa operação não custa apenas RAM. O custo de CPU para mover dados entre kernel space e user space em larguras de banda de NVMe Gen5 (14GB/s+) é brutal. Você está transformando sua CPU em um despachante de luxo.

      Se o dado for alterado pelo banco, ele é escrito no buffer do banco, depois copiado para o ZFS, que o coloca no ARC/TXG antes de ir para o disco. Se o banco decidir descartar o dado da memória dele porque não é mais relevante, o ZFS ainda o mantém no ARC, achando que está ajudando.

      Fig 1. O caminho do I/O: A ineficiência do Buffer Duplo vs. a precisão do Direct I/O. Fig 1. O caminho do I/O: A ineficiência do Buffer Duplo vs. a precisão do Direct I/O.

      Fig 1. O caminho do I/O: A ineficiência do Buffer Duplo vs. a precisão do Direct I/O.

      Por que limitar o zfs_arc_max é apenas um curativo temporário

      Durante a última década, a "solução" padrão em todos os fóruns, do Reddit à lista de discussão do FreeBSD, foi: "Seta o zfs_arc_max para 10% da RAM e vida que segue".

      Eu odeio essa abordagem.

      Primeiro porque é estática. Se você define o ARC para 4GB em um servidor de 64GB, e por algum motivo seu banco de dados está ocioso ou foi desligado para manutenção, o ZFS não pode usar os 60GB livres para acelerar outras operações de arquivo (como um backup ou rsync). Você transformou memória dinâmica em um recurso engessado.

      Segundo, limitar o tamanho do ARC não resolve o problema do overhead de CPU. O memcpy continua acontecendo. O "context switch" continua acontecendo. Você apenas limitou o quanto de memória o ZFS pode "roubar", mas não tornou o caminho do dado mais eficiente. É como colocar um limitador de velocidade em um carro com o freio de mão puxado; você não resolveu o atrito.

      Como o suporte a Direct I/O no OpenZFS 2.4 elimina a cópia dupla

      A flag O_DIRECT existe no Linux há eras. Sistemas de arquivos como XFS e EXT4 a suportam nativamente. O ZFS, em sua arrogância arquitetural (e complexidade de Copy-on-Write), resistiu por muito tempo. A implementação era complexa porque o ZFS precisa garantir a integridade dos dados (checksums) e atomicidade, coisas que o Direct I/O tradicionalmente ignora para ganhar velocidade.

      No OpenZFS 2.4, o suporte a Direct I/O amadureceu para produção. O que acontece agora quando uma aplicação chama open() com a flag O_DIRECT:

      1. Bypass do ARC: O ZFS reconhece a flag e entende que não deve alocar espaço no ARC para esses dados.

      2. Zero-Copy (quase): O dado é transferido do disco diretamente para o buffer da aplicação (user space) via DMA, ou com o mínimo de intervenção possível do kernel.

      3. Manutenção da Integridade: O ZFS ainda verifica o checksum. Isso é inegociável. Mas ele faz isso "on the fly", sem precisar estacionar o dado no cache primeiro.

      💡 Dica Pro: Para ativar isso, você não precisa formatar nada. É uma mudança de comportamento na aplicação. No PostgreSQL, por exemplo, certifique-se de que sua versão suporte Direct I/O e configure wal_sync_method ou parâmetros equivalentes de I/O direto se disponíveis na sua versão específica e driver de armazenamento.

      O resultado imediato é que o shared_buffers do seu banco de dados passa a ser a única fonte de verdade na RAM. Se você tem 128GB de RAM, pode alocar 100GB para o banco com segurança, sabendo que o sistema de arquivos não vai tentar competir por esse espaço.

      Métricas de latência e uso de CPU após o bypass do cache

      Não acredite em marketing, acredite em perf e iostat. Ao ativar o Direct I/O em cargas de trabalho de banco de dados pesadas (OLTP), observamos dois fenômenos distintos:

      1. Queda no Uso de CPU (System Time): A redução de tempo de CPU gasto em modo kernel (sy no top) é drástica. Menos cópias de memória significam que a CPU passa mais tempo processando queries SQL e menos tempo movendo bits de um lado para o outro.

      2. Latência de Cauda (Tail Latency): Aqui a coisa fica interessante. Sem o ARC, você perde aquele "acerto de sorte" de ler um dado que estava no cache do disco mas não no do banco. Porém, a latência média se torna muito mais previsível. O "jitter" diminui. Para DBAs sérios, consistência é melhor que picos de velocidade aleatórios.

      Em testes com NVMe Gen4, a taxa de transferência sustentada aumenta porque o gargalo da memória (memory bandwidth saturation) é aliviado. O barramento de memória não está mais saturado copiando dados duplicados.

      O veredito do sysadmin

      O Direct I/O no ZFS não é uma varinha mágica para todos. Se você tem um file server genérico (Samba/NFS) servindo arquivos de Word para o departamento de RH, não toque nisso. O ARC é seu melhor amigo nesses casos, pois os clientes são burros e não fazem cache direito.

      Mas para infraestrutura de banco de dados, virtualização (KVM/QEMU com volumes Zvol) e aplicações de armazenamento de objetos (MinIO), o OpenZFS 2.4 com Direct I/O é a carta de alforria da sua memória RAM. Pare de tunar zfs_arc_max como se fosse 2015. Deixe o banco gerenciar o cache dele e deixe o ZFS cuidar apenas de garantir que os bits no disco não apodreçam.

      Seu servidor não precisa de mais RAM. Ele precisa de menos burocracia no I/O.

      Referências & Leitura Complementar

      • OpenZFS GitHub Pull Request #15362: Implementação inicial e discussões sobre Direct I/O (O_DIRECT) no ZFS on Linux.

      • PostgreSQL Documentation (Resource Consumption): Detalhes sobre shared_buffers e interação com o cache do kernel.

      • J. Bonwick, B. Moore (Sun Microsystems): "ZFS: The Last Word in File Systems" (Para entender por que o ARC foi desenhado daquela forma originalmente).

      Perguntas Frequentes (FAQ)

      1. O Direct I/O desativa o L2ARC? Sim, para as leituras feitas com O_DIRECT. O L2ARC é uma extensão do ARC. Se o dado não entra no ARC, ele não vai para o L2ARC. Se sua performance depende pesadamente de um SSD de cache L2, avalie se o Direct I/O vale a pena ou se você deve apenas aumentar a RAM.

      2. Preciso recompilar o kernel para usar isso no OpenZFS 2.4? Geralmente não, desde que você esteja usando os pacotes DKMS ou kmod oficiais da sua distribuição que já incluam a versão 2.4 (ou superior). Verifique com zfs --version.

      3. Isso aumenta o desgaste do meu SSD? Teoricamente, o ARC absorve algumas leituras repetidas. Sem ele, essas leituras batem no disco. Porém, bancos de dados modernos têm caches internos enormes. Se o banco não achou na RAM dele, ele teria que ir ao disco de qualquer jeito. O impacto no desgaste de leitura é negligenciável em configurações bem ajustadas.

      4. Posso usar Direct I/O em datasets comprimidos (LZ4/ZSTD)? Sim. O ZFS descomprime o bloco antes de entregá-lo ao buffer do usuário. O overhead de descompressão ainda existe, mas você economiza a cópia da memória. A integridade dos dados continua garantida.

      #OpenZFS 2.4 #Direct I/O #O_DIRECT #ZFS ARC Tuning #PostgreSQL Performance #Database Storage Optimization #NVMe Latency
      Roberto Uchoa
      Assinatura Técnica

      Roberto Uchoa

      Sysadmin Veterano (Anti-Hype)

      "Sobrevivente da bolha pontocom e do hype do Kubernetes. Troco qualquer arquitetura de microsserviços 'inovadora' por um script bash que funciona sem falhas há 15 anos. Uptime não é opcional."