NULL não é grátis: entendendo o NULL Bitmap no MySQL

“O custo invisível dos campos NULL no MySQL”

A maioria dos desenvolvedores sabe que um campo NULL “economiza” espaço por não armazenar um valor efetivo, mas poucos sabem que existe um custo associado ao controle desses valores através do NULL Bitmap.

Muitos profissionais acreditam que declarar todos os campos como NULL não possui impacto de armazenamento. Na prática, o MySQL precisa manter uma estrutura interna para informar quais colunas possuem valores nulos em cada registro.

Essa estrutura é conhecida como NULL Bitmap.

Para cada coluna que permite NULL, o InnoDB reserva 1 bit dentro desse bitmap.

A fórmula é simples:

Bytes do NULL Bitmap = (Quantidade de colunas NULL + 7) / 8

Arredondando sempre para cima.

Exemplos:

Colunas NULL

Bitmap

1 a 8

1 byte

9 a 16

2 bytes

17 a 24

3 bytes

25 a 32

4 bytes

 

Essa fórmula parece estranha à primeira vista, mas é bem simples.

O MySQL utiliza 1 bit para cada coluna que permite NULL.

Lembre-se:

1 byte = 8 bits

Então o cálculo basicamente responde:

“Quantos bytes preciso para armazenar N bits?”

 

Exemplo 1:

1 coluna NULL

nome VARCHAR(100) NULL

Precisamos de:

1 bit

Mas não existe armazenamento de 1 bit isolado.

O menor espaço reservado será:

1 byte

Resultado:

(1 + 7) / 8 = 1

 

CRIANDO O CENÁRIO PARA TESTES:

Tabela tb_clientes com colunas permitindo NULL.

Tabela tb_clientes_not_null com colunas permitindo NULL.

Inserindo 500 (milhões de linhas na tabela tb_clientes com colunas NULL)

A estimativa de tempo decorrido para a máquina a qual foi feito a inserção de dados na tabela tb_clientes foi de 03:11:10. Um tempo razoável.

Inserindo 500 (milhões de linhas na tabela tb_clientes_not_null com colunas NOT NULL)

A estimativa de tempo decorrido para a máquina a qual foi feito a inserção de dados na tabela tb_clientes_not_null foi de 02:15:49. 

Inserindo500 (milhões de linhas na tabela tb_clientes_vazio com colunas permitindo NULL)

A estimativa de tempo decorrido para a máquina a qual foi feito a inserção de dados na tabela tb_clientes_not_null foi de 02:31:17. 

Query para verificação dos tamanhos das respectivas tabelas:

Observem que o primeiro script de inserção na tabela tb_clientes tem em sua composição 9 colunas permitindo NULL.

Mesmo que nenhuma delas esteja nula, o InnoDB precisará reservar:

8 bits = 1 byte

por registro apenas para controlar o estado dessas colunas.

Agora imagine:

  • 500 milhões de registros
  • 1 byte por registro

Teríamos aproximadamente:

500.000.000 bytes ≈ 476MB apenas para o bitmap de NULL.

Os números ficaram muito interessantes e sustentam uma conclusão técnica bastante forte.

Comparativo estatístico

Cénario Tamanho (GB) Diferença Crescimento (%)
Casmpos NULL (tb_clientes) 19,34 GB – –
Campos NOT NULL + string vazia (tb_clientes_vazio) 23,87 GB + 4,53 GB + 23,42 %
Campos NOT NULL + ‘0’ (tb_clientes_not_null) 27,38 GB + 8,04 GB + 41,57 %

Além disso:

  • String vazia ('') → Valor '0'
    • Crescimento de 3,51 GB  (Campos NOT NULL + ‘0’ ) – (Campos NOT NULL + string vazia ”)
    • Equivale a aproximadamente 14,71% de aumento.

Outro ponto muito interessante

Muita gente pensa:

“Vou deixar tudo NULL porque um dia posso precisar.”

Mas isso gera:

  • NULL Bitmap maior
  • Mais lógica nas consultas
  • Mais uso de IS NULL
  • Mais complexidade em índices
  • Maior ocupação em tabelas gigantes

Em muitos cenários, um valor padrão faz mais sentido:

Mas você pode estar se perguntando: qual a relação entre performance e o espaço ocupado em disco pelos campos que permitem valores NULL? Afinal, até aqui estamos falando apenas de bits e bytes adicionais de armazenamento.

A resposta é simples: tudo está diretamente relacionado!

O mecanismo de armazenamento InnoDB organiza os dados em páginas de 16 KB. À medida que cada registro ocupa mais espaço, mesmo que seja apenas alguns bytes adicionais provenientes do NULL Bitmap, a quantidade de registros armazenados por página diminui. Como consequência, o banco de dados precisa criar um número maior de páginas para armazenar a mesma quantidade de informações.

Esse crescimento impacta diretamente as operações de leitura. Quanto mais páginas existirem, maior será a quantidade de páginas que o MySQL precisará acessar para localizar e retornar os dados solicitados por uma consulta. Isso significa mais operações de I/O, maior consumo de memória no Buffer Pool e, potencialmente, um aumento no tempo de resposta das consultas.

Em tabelas pequenas, esse impacto costuma ser imperceptível. Entretanto, em ambientes que armazenam centenas de milhões ou até bilhões de registros, alguns bytes adicionais por linha podem representar gigabytes extras de armazenamento e milhões de páginas adicionais sendo gerenciadas pelo mecanismo de banco de dados.

Em outras palavras, quando falamos sobre otimização de armazenamento, estamos também falando sobre otimização de performance. No InnoDB, espaço e desempenho caminham lado a lado.

Conclusão

Os resultados obtidos demonstram que decisões aparentemente simples de modelagem podem produzir impactos expressivos quando a volumetria cresce. Em uma base com aproximadamente 500 milhões de registros, a tabela cujos campos permaneceram como NULL ocupou 19,34 GB, enquanto a mesma estrutura utilizando strings vazias consumiu 23,87 GB, um aumento de 4,53 GB, equivalente a 23,42%. Quando os campos passaram a armazenar o valor ‘0’, o espaço ocupado chegou a 27,38 GB, representando um crescimento de 8,04 GB ou 41,57% em relação ao cenário com valores NULL.

Embora o InnoDB utilize o NULL Bitmap, que adiciona um pequeno custo para controlar quais colunas possuem valores NULL, esse overhead é significativamente menor do que o espaço necessário para armazenar valores efetivos em cada registro. Em cenários de alta volumetria, poucos bytes adicionais por linha se transformam em gigabytes de armazenamento, aumentando a quantidade de páginas de 16 KB gerenciadas pelo InnoDB e, consequentemente, o volume de dados que precisam ser lidos durante as consultas.

A principal lição é que otimização de banco de dados não se resume à escrita de consultas eficientes. Ela começa na modelagem dos dados. Em ambientes com centenas de milhões ou bilhões de registros, cada byte economizado por registro representa gigabytes poupados em disco, menos páginas para o mecanismo de armazenamento gerenciar e maior eficiência nas operações de leitura e escrita.

Em bancos de dados de grande porte, a diferença entre alguns bytes por registro e uma boa modelagem pode significar dezenas de gigabytes, milhões de páginas a menos para o InnoDB gerenciar e um ganho real de eficiência. Na engenharia de dados, cada byte conta.

COMPARTILHE:
Picture of Thiago Rafael

Thiago Rafael

Nordestino, paraibano e pai de pet do Woody (Dachshund de 9 anos). Professor Universitário e Engenheiro de Dados Sênior, apaixonado por bancos de dados, com sólida experiência em engenharia de dados, integração de sistemas, modelagem de dados, otimização de consultas e arquitetura de soluções orientadas a dados. Possui MBA em Engenharia e Ciência de Dados, MBA em Administração e Gerenciamento de Bancos de Dados e MBA em Business Intelligence e Big Data. Acredita que compreender a arquitetura interna de um banco de dados é tão importante quanto saber escrever consultas SQL eficientes, motivo pelo qual dedica parte do seu tempo ao estudo, ensino e à produção de conteúdo técnico sobre MySQL e tecnologias relacionadas.
Picture of Thiago Rafael

Thiago Rafael

Nordestino, paraibano e pai de pet do Woody (Dachshund de 9 anos). Professor Universitário e Engenheiro de Dados Sênior, apaixonado por bancos de dados, com sólida experiência em engenharia de dados, integração de sistemas, modelagem de dados, otimização de consultas e arquitetura de soluções orientadas a dados. Possui MBA em Engenharia e Ciência de Dados, MBA em Administração e Gerenciamento de Bancos de Dados e MBA em Business Intelligence e Big Data. Acredita que compreender a arquitetura interna de um banco de dados é tão importante quanto saber escrever consultas SQL eficientes, motivo pelo qual dedica parte do seu tempo ao estudo, ensino e à produção de conteúdo técnico sobre MySQL e tecnologias relacionadas.

PARTICIPE

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

POSTADO POR
Picture of Thiago Rafael

Thiago Rafael

Nordestino, paraibano e pai de pet do Woody (Dachshund de 9 anos). Professor Universitário e Engenheiro de Dados Sênior, apaixonado por bancos de dados, com sólida experiência em engenharia de dados, integração de sistemas, modelagem de dados, otimização de consultas e arquitetura de soluções orientadas a dados. Possui MBA em Engenharia e Ciência de Dados, MBA em Administração e Gerenciamento de Bancos de Dados e MBA em Business Intelligence e Big Data. Acredita que compreender a arquitetura interna de um banco de dados é tão importante quanto saber escrever consultas SQL eficientes, motivo pelo qual dedica parte do seu tempo ao estudo, ensino e à produção de conteúdo técnico sobre MySQL e tecnologias relacionadas.
PARCEIROS