Arquitetura de SGBDs relacionais

Quando você digita um simples SELECT e o resultado aparece na tela em milissegundos, há por trás uma orquestra de componentes de software trabalhando em conjunto. Neste módulo eu quero abrir a “tampa” do motor e te mostrar como um Sistema Gerenciador de Banco de Dados (SGBD) relacional é arquitetado por dentro. Esse é um tema que cai com frequência em prova porque amarra tudo o que você já estudou: o caminho que uma consulta percorre, como as transações são protegidas, como os dados ficam organizados no disco e, sobretudo, a famosa arquitetura de três esquemas da ANSI/SPARC. Vou conduzir você camada por camada, do nível físico ao lógico, e depois te mostrar como esse projeto se estende para os mundos distribuído, paralelo e in-memory. Repare comigo que a arquitetura não é um detalhe de implementação acadêmico: é o que explica por que esses sistemas são confiáveis, rápidos e duradouros há mais de cinquenta anos.

O que entendemos por arquitetura de um SGBD

Falar de arquitetura é falar de organização: quais peças existem, o que cada uma faz e como elas conversam. Um SGBD relacional não é um programa monolítico; ele é um conjunto de subsistemas especializados, cada um responsável por uma fatia bem definida do trabalho de gerenciar dados. Eu gosto de pensar nisso como uma fábrica. Há um setor que recebe e interpreta os pedidos (as consultas SQL), um setor que planeja a melhor forma de produzir, um setor que cuida da memória de trabalho, um setor que organiza o estoque no almoxarifado (o disco) e um setor de segurança e auditoria que garante que nada se perca em caso de incêndio. Quando essas partes funcionam em harmonia, o resultado é desempenho, consistência e durabilidade.

Definição. A arquitetura de um SGBD descreve seus componentes internos, suas responsabilidades e suas interações, além de como os dados são representados em diferentes níveis de abstração — do esquema visto pelo usuário até a organização física em blocos de disco.

Os componentes de um SGBD relacional

Vou começar mapeando as peças. Pense numa pilha de camadas: no topo chegam os comandos, e na base estão os bits no disco. Entre os dois extremos ficam os gerenciadores que fazem a ponte.

flowchart TD
    A[Aplicacao ou usuario envia SQL] --> B[Processador de consultas]
    B --> C[Parser e analise semantica]
    B --> D[Otimizador de consultas]
    B --> E[Motor de execucao]
    E --> F[Gerenciador de transacoes]
    E --> G[Gerenciador de buffer]
    G --> H[Gerenciador de armazenamento]
    F --> I[Gerenciador de recuperacao e log]
    H --> J[(Arquivos em disco)]
    I --> J
    K[Dicionario de dados] --- B
    K --- F
    K --- H

O processador de consultas é a porta de entrada. Ele recebe o texto SQL, verifica se a sintaxe está correta no parser, confere no analisador semântico se as tabelas e colunas citadas existem e se você tem permissão para acessá-las, e então entrega o trabalho ao otimizador. O otimizador é, na minha visão, o componente mais engenhoso de todo o sistema: ele gera vários planos de execução possíveis para a mesma consulta e escolhe o de menor custo estimado, considerando estatísticas sobre os dados, índices disponíveis e os custos de leitura em disco e de processamento. Em seguida o motor de execução percorre esse plano, pedindo páginas de dados às camadas inferiores.

O gerenciador de transações garante que toda unidade lógica de trabalho respeite as propriedades ACID, coordenando bloqueios ou versões (MVCC) para que transações concorrentes não se atrapalhem. O gerenciador de buffer mantém em memória RAM as páginas mais acessadas, evitando ir ao disco a cada operação — é uma camada de cache decisiva para o desempenho. Logo abaixo, o gerenciador de armazenamento cuida da organização física: aloca espaço, mantém os índices em disco e executa o I/O de baixo nível. O gerenciador de recuperação registra cada mudança num log antes de aplicá-la (o Write-Ahead Logging) e usa esse log mais os checkpoints para reconstruir um estado consistente após uma falha. Costurando tudo, há o dicionário de dados (ou catálogo), uma base de dados sobre o próprio banco: ele guarda as definições de tabelas, colunas, tipos, restrições, visões, índices, usuários e privilégios. Todo componente consulta o dicionário o tempo todo.

Componente Responsabilidade central Trabalha junto com
Parser / analisador Validar sintaxe e semântica do SQL Dicionário de dados
Otimizador Escolher o plano de execução de menor custo Estatísticas e dicionário
Motor de execução Executar o plano e produzir resultados Buffer e armazenamento
Gerenciador de transações Garantir ACID e isolamento Recuperação e log
Gerenciador de buffer Cache de páginas em RAM Armazenamento
Gerenciador de armazenamento Organização física, alocação, I/O Disco
Gerenciador de recuperação Log, checkpoint, redo/undo Buffer e disco
Dicionário de dados Metadados do banco Todos os demais

O caminho de uma consulta: do parse à execução

Quero agora seguir uma consulta passo a passo, porque entender esse fluxo é entender o coração do SGBD. Imagine que você submeteu o comando abaixo.

SELECT c.nome, SUM(p.valor) AS total
FROM cliente c
JOIN pedido p ON p.cliente_id = c.id
WHERE c.cidade = 'São Paulo'
GROUP BY c.nome
HAVING SUM(p.valor) > 1000;

O que acontece com ela? O texto primeiro vira uma árvore. Depois essa árvore é validada, reescrita numa forma equivalente mais eficiente, otimizada com escolha de índices e métodos de junção, traduzida num plano de baixo nível e finalmente executada. Veja o percurso completo.

flowchart LR
    A[SQL recebido] --> B[Parsing sintatico]
    B --> C[Analise semantica e permissoes]
    C --> D[Reescrita da consulta]
    D --> E[Otimizacao baseada em custo]
    E --> F[Geracao do plano de execucao]
    F --> G[Execucao no motor]
    G --> H[Acesso a dados via buffer e disco]
    H --> I[Resultado retornado]

Na etapa de otimização, o sistema decide questões como: vale a pena varrer a tabela pedido inteira ou usar um índice em cliente_id? A junção deve ser feita com nested loop, merge join ou hash join? Convém aplicar o filtro cidade = 'São Paulo' antes da junção, reduzindo o volume de linhas que entram nela? Esse último truque, empurrar o predicado para perto da fonte dos dados, chama-se pushdown de predicados e quase sempre acelera a consulta. O otimizador também pode reordenar junções para minimizar o tamanho dos resultados intermediários — a ordem em que combinamos as tabelas tem impacto enorme no custo.

Atenção. A consulta SQL é declarativa: você diz o que quer, não como obter. Quem decide o “como” é o otimizador. Por isso duas consultas com o mesmo resultado podem ter desempenho radicalmente diferente, e por isso manter estatísticas atualizadas é tão importante — elas alimentam as estimativas de custo.

Arquitetura física: arquivos, páginas, blocos e registros

Vamos descer ao subsolo, onde os dados realmente moram. No nível físico, o banco é um conjunto de arquivos no sistema de arquivos do sistema operacional. Esses arquivos são divididos em páginas (também chamadas de blocos), tipicamente de 4 KB a 16 KB. A página é a unidade mínima de transferência entre disco e memória: quando o SGBD precisa de um único registro, ele traz a página inteira que o contém para o buffer. Cada página tem um cabeçalho com metadados (identificador, checksum), uma área com os registros de dados, ponteiros e um espaço livre reservado para crescimento.

flowchart TD
    A[Banco de dados] --> B[Arquivo de dados]
    B --> C[Pagina ou bloco de 4KB a 16KB]
    C --> D[Cabecalho com metadados]
    C --> E[Registros das tuplas]
    C --> F[Espaco livre]
    E --> G[Campos de cada registro]

A forma como as páginas de uma tabela são organizadas define a estratégia de acesso. Num heap file, os registros entram na ordem de chegada, sem ordenação — é ótimo para inserir rápido, mas péssimo para buscar sem índice. Num arquivo ordenado, mantém-se a ordem por uma chave, o que acelera buscas por intervalo mas encarece inserções. No hashing, uma função distribui os registros em buckets, perfeito para busca por igualdade. E há o armazenamento orientado a coluna, em que cada coluna é guardada separadamente, ideal para análises que tocam poucas colunas de muitíssimas linhas.

É aqui também que vivem os índices, estruturas auxiliares que aceleram a localização de registros. A B+Tree é a campeã absoluta porque atende bem tanto buscas por igualdade quanto por intervalo, mantendo-se balanceada. Já o índice hash só serve à igualdade, e o índice bitmap brilha em colunas de baixa cardinalidade.

Organização Forte em Fraca em
Heap file Inserção rápida Busca sem índice
Arquivo ordenado Busca por intervalo Manter ordem em inserções
Hashing Busca por igualdade Busca por intervalo
Orientado a coluna Consultas analíticas Atualização de linhas inteiras

Como o acesso a disco é o maior gargalo, o gerenciador de buffer aplica políticas como a substituição LRU (descartar a página menos recentemente usada) e o prefetching (antecipar páginas). E aqui entra um princípio que você precisa gravar: o Write-Ahead Logging exige que toda alteração seja registrada no log antes de ser gravada na página de dados. É esse protocolo que torna a durabilidade possível mesmo quando a energia cai no meio de uma escrita.

Arquitetura lógica e os três esquemas da ANSI/SPARC

Agora subimos de volta para a abstração mais importante deste módulo. Em 1975, o comitê ANSI/SPARC propôs uma arquitetura de três níveis de esquema que até hoje é a espinha dorsal conceitual de qualquer SGBD relacional. A ideia é separar como o usuário vê os dados, como o banco os organiza logicamente e como eles são fisicamente armazenados. Essa separação é o que dá ao sistema sua flexibilidade lendária.

flowchart TD
    A[Nivel externo: visoes de cada grupo de usuarios] --> B[Nivel conceitual: esquema logico global]
    B --> C[Nivel interno: estruturas fisicas em disco]
    A -. independencia logica .-> B
    B -. independencia fisica .-> C

No nível externo ficam as visões: cada grupo de usuários enxerga apenas a parte do banco que lhe interessa, com detalhes desnecessários ocultados. No nível conceitual mora o esquema lógico completo — todas as tabelas, colunas, relacionamentos e restrições do banco, independente de implementação física. No nível interno estão as estruturas de armazenamento reais: arquivos, índices, métodos de acesso, organização das páginas. Um esquema é essa descrição estrutural; já a instância é o estado concreto dos dados num dado momento. O esquema muda raramente; a instância muda a cada INSERT ou UPDATE.

O nível externo se materializa em SQL através de visões. Veja como eu criaria uma visão que expõe a uma equipe de marketing apenas os clientes ativos de São Paulo, sem revelar colunas sensíveis.

CREATE VIEW vw_clientes_sp AS
SELECT id, nome, email, cidade
FROM cliente
WHERE cidade = 'São Paulo' AND ativo = TRUE;

A equipe consulta vw_clientes_sp como se fosse uma tabela, sem nunca tocar na tabela base nem ver as colunas financeiras. Isso resolve segurança e simplicidade de uma vez só.

Definição-chave: independência de dados. É a capacidade de alterar um nível sem afetar os níveis superiores. A independência lógica permite mudar o esquema conceitual (por exemplo, adicionar uma coluna a uma tabela) sem quebrar as visões e aplicações existentes. A independência física permite mudar o esquema interno (reorganizar índices, trocar o método de acesso) sem afetar o esquema conceitual. É essa propriedade que deixa o banco evoluir por anos sem reescrever os sistemas que dependem dele.

Observe na figura que a independência lógica fica na fronteira entre o externo e o conceitual, e a independência física entre o conceitual e o interno. Na prática, a independência física é mais fácil de obter; a lógica é mais difícil, porque alterações no esquema conceitual costumam ter eco nas aplicações. Esse é um ponto que examinadores adoram inverter para te confundir, então fixe bem qual independência protege qual fronteira.

Arquiteturas avançadas e como o sistema se distribui

A arquitetura que descrevi até agora é, em essência, centralizada: um único processo, um único servidor, tudo num lugar só. SQLite é o exemplo clássico — ele nem servidor tem, roda embutido na própria aplicação e guarda todo o banco num único arquivo. Mas as demandas modernas de escala e disponibilidade levaram a arranjos mais sofisticados. Vou te mostrar a evolução.

flowchart TD
    A[Centralizada: tudo em um servidor] --> B[Cliente-servidor: clientes pedem, servidor processa]
    B --> C[Distribuida: dados fragmentados em varios nos]
    B --> D[Paralela: varios processadores no mesmo sistema]
    C --> E[In-memory e NewSQL: alto desempenho e escala]
    D --> E

Na arquitetura cliente-servidor, que domina o mercado, o servidor do banco roda numa máquina dedicada e várias aplicações clientes se conectam a ele pela rede, enviando SQL e recebendo resultados. A arquitetura distribuída vai além: os dados são fragmentados (horizontalmente, dividindo linhas; ou verticalmente, dividindo colunas) e espalhados por múltiplos nós, possivelmente em locais geográficos diferentes, muitas vezes com replicação para tolerância a falhas. O grande desafio aqui é manter a consistência: transações que tocam vários nós usam o protocolo de confirmação em duas fases (Two-Phase Commit) para garantir que ou todos confirmam, ou todos desfazem. O sistema também busca transparência, escondendo do usuário que os dados estão fragmentados, replicados e espalhados.

A arquitetura paralela usa vários processadores dentro do mesmo sistema para acelerar o trabalho. Costumamos classificá-la pelo compartilhamento de recursos: shared-memory (processadores compartilham memória, comunicação rápida mas escala limitada), shared-disk (memória própria, disco comum) e shared-nothing (cada nó tem memória e disco próprios, altamente escalável — é o modelo preferido dos sistemas massivamente paralelos). O paralelismo pode ocorrer dentro de uma consulta (dividindo uma junção entre núcleos) ou entre consultas (rodando várias ao mesmo tempo).

Por fim, as arquiteturas in-memory mantêm os dados primariamente na RAM, eliminando a latência de disco e oferecendo desempenho de ordens de magnitude superior — o SAP HANA é o exemplo emblemático. Como a memória é volátil, esses sistemas ainda gravam log em disco para garantir durabilidade. E há a classe NewSQL, que busca a escalabilidade horizontal do NoSQL sem abrir mão das garantias ACID, usando shared-nothing, protocolos de consenso como Paxos ou Raft e MVCC.

Arquitetura Característica central Exemplo
Centralizada / embutida Tudo num processo único SQLite
Cliente-servidor Servidor dedicado, clientes via rede PostgreSQL, MySQL, SQL Server
Distribuída Dados fragmentados e replicados em nós Google Cloud Spanner
Paralela Vários processadores no mesmo sistema Oracle RAC, IBM Db2
In-memory Dados primariamente em RAM SAP HANA
NewSQL Escala horizontal com ACID forte Spanner, sistemas shared-nothing

Esses exemplos não são puros: o Oracle combina paralelismo, in-memory e clustering; o PostgreSQL é cliente-servidor extensível com replicação; o Amazon Aurora separa armazenamento e computação na nuvem. O que você precisa perceber é que todos partem do mesmo núcleo arquitetural — processador de consultas, gerenciadores de transação, buffer, armazenamento e recuperação — e o estendem para vencer desafios de escala, disponibilidade e desempenho.

Síntese para a prova

Vou amarrar os pontos que mais caem. Um SGBD relacional é composto por componentes especializados que conversam: o processador de consultas (com parser, otimizador e motor de execução), o gerenciador de transações (garante ACID e isolamento via locks ou MVCC), o gerenciador de buffer (cache de páginas em RAM, política LRU), o gerenciador de armazenamento (organização física e I/O), o gerenciador de recuperação (Write-Ahead Logging e checkpoints) e o dicionário de dados (metadados de tudo). O caminho de uma consulta vai do parsing à análise semântica, reescrita, otimização baseada em custo, geração do plano e execução — lembre que o SQL é declarativo e quem decide o “como” é o otimizador.

No nível físico, dados moram em arquivos divididos em páginas de 4 KB a 16 KB, contendo registros; índices B+Tree aceleram buscas por igualdade e intervalo. O conceito mais cobrado é a arquitetura de três esquemas ANSI/SPARC: nível externo (visões), conceitual (esquema lógico global) e interno (estruturas físicas). Daí derivam a independência lógica (mudar o conceitual sem afetar as visões) e a independência física (mudar o interno sem afetar o conceitual) — não troque as duas. As CREATE VIEW implementam o nível externo, garantindo segurança e simplicidade. Por fim, conheça as arquiteturas centralizada, cliente-servidor, distribuída (com fragmentação, replicação e Two-Phase Commit), paralela (shared-memory, shared-disk, shared-nothing) e in-memory, sabendo associar cada uma a exemplos como SQLite, PostgreSQL, Spanner e SAP HANA.