Relacional vs Não Relacional
A escolha do banco de dados é uma das decisões mais importantes durante o desenvolvimento de um sistema. Ela influencia diretamente a forma como os dados são armazenados, consultados, escalados e mantidos.
De forma geral, podemos dividir os bancos de dados em duas grandes categorias:
- Bancos relacionais (SQL)
- Bancos não relacionais (NoSQL)
A principal diferença não é simplesmente "SQL é antigo e NoSQL é moderno". Cada modelo resolve problemas diferentes e possui seus próprios trade-offs.
Em System Design, a pergunta não é "qual banco é melhor?", mas sim:
"Qual modelo de armazenamento atende melhor aos requisitos do meu sistema?"
1. Bancos de Dados Relacionais
Os bancos relacionais organizam os dados em tabelas, compostas por linhas e colunas.
Exemplos:
- PostgreSQL
- MySQL
- MariaDB
- Oracle Database
- SQL Server
Um exemplo simples:
USERS
┌────┬──────────┬────────────────────┐
│ ID │ NAME │ EMAIL │
├────┼──────────┼────────────────────┤
│ 1 │ João │ joao@email.com │
│ 2 │ Maria │ maria@email.com │
└────┴──────────┴────────────────────┘
Podemos ter outra tabela relacionada:
ORDERS
┌────┬─────────┬────────┬────────────┐
│ ID │ USER_ID │ TOTAL │ CREATED_AT │
├────┼─────────┼────────┼────────────┤
│ 10 │ 1 │ 150.00 │ ... │
│ 11 │ 1 │ 200.00 │ ... │
│ 12 │ 2 │ 80.00 │ ... │
└────┴─────────┴────────┴────────────┘
Nesse caso:
USERS
│
│ 1:N
▼
ORDERS
O USER_ID relaciona um pedido ao usuário que realizou a compra.
É justamente essa capacidade de representar relações entre entidades que dá origem ao nome "banco relacional".
2. Schema Estruturado
Uma das principais características dos bancos relacionais é possuir um schema definido.
Por exemplo:
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL
);
Nesse modelo, cada registro precisa seguir uma estrutura definida.
Isso oferece algumas vantagens importantes:
- Consistência dos dados
- Validação de tipos
- Constraints
- Chaves primárias
- Chaves estrangeiras
- Relacionamentos bem definidos
- Maior previsibilidade das consultas
Por outro lado, mudanças na estrutura podem exigir alterações no schema.
Por exemplo:
Versão 1
USER
├── id
├── name
└── email
Versão 2
USER
├── id
├── name
├── email
└── phone
Adicionar uma nova coluna normalmente exige uma alteração na estrutura da tabela.
3. SQL e Relacionamentos
Os bancos relacionais são extremamente fortes quando precisamos trabalhar com dados relacionados.
Imagine um e-commerce:
USER
│
├── ORDERS
│ │
│ └── ORDER_ITEMS
│ │
│ └── PRODUCTS
│
└── ADDRESSES
Uma consulta pode precisar combinar várias tabelas:
SELECT
users.name,
orders.id,
orders.total
FROM users
JOIN orders
ON users.id = orders.user_id;
Esse tipo de operação é conhecido como JOIN.
Os JOINs são extremamente poderosos, mas também podem se tornar mais custosos em sistemas muito grandes ou em consultas extremamente complexas.
4. Transações e ACID
Uma das grandes vantagens dos bancos relacionais é o suporte robusto a transações.
As transações normalmente são associadas às propriedades ACID:
A — Atomicity
C — Consistency
I — Isolation
D — Durability
Atomicidade
Uma operação deve acontecer completamente ou não acontecer.
Exemplo:
Transferência bancária
Conta A
- R$ 100
Conta B
+ R$ 100
Se o dinheiro for retirado da conta A, mas ocorrer um erro antes de adicionar na conta B, a transação pode ser revertida.
BEGIN TRANSACTION
A - R$100
B + R$100
COMMIT
Se algo der errado:
ROLLBACK
O sistema retorna ao estado anterior.
Consistência
Os dados devem respeitar as regras definidas pelo banco.
Por exemplo:
email UNIQUE
Não podemos ter dois usuários com o mesmo email.
Ou:
FOREIGN KEY
Um pedido não pode apontar para um usuário inexistente.
Isolamento
Transações concorrentes não devem interferir umas nas outras de maneira incorreta.
Imagine dois usuários tentando comprar o último produto disponível:
Estoque = 1
Cliente A ──┐
├── Compra
Cliente B ──┘
O banco precisa garantir que o sistema não venda duas unidades quando existe apenas uma.
Durabilidade
Depois que uma transação é confirmada, os dados devem permanecer salvos mesmo em caso de falha do sistema.
COMMIT
│
▼
Dado persistido
│
▼
Servidor reinicia
│
▼
Dado continua existindo
5. Bancos de Dados Não Relacionais
Bancos NoSQL surgiram para resolver problemas que nem sempre são bem atendidos pelo modelo relacional tradicional.
Alguns exemplos:
- MongoDB
- Redis
- Cassandra
- DynamoDB
- Neo4j
Porém, é importante entender que NoSQL não significa simplesmente "banco sem SQL".
O termo normalmente está relacionado a modelos de armazenamento que não seguem necessariamente o modelo tradicional de tabelas relacionais.
Existem diferentes tipos de bancos NoSQL.
6. Tipos de Bancos NoSQL
6.1 Document Database
Exemplos:
- MongoDB
- Couchbase
Os dados são armazenados como documentos, geralmente semelhantes a JSON.
Exemplo:
{
"id": 1,
"name": "João",
"email": "joao@email.com",
"orders": [
{
"id": 10,
"total": 150
},
{
"id": 11,
"total": 200
}
]
}
Aqui, informações relacionadas podem ser armazenadas juntas.
Em vez de:
USERS
│
└── JOIN
│
▼
ORDERS
Podemos ter:
USER DOCUMENT
│
├── name
├── email
│
└── orders[]
Isso pode simplificar determinadas consultas.
6.2 Key-Value
Exemplos:
- Redis
- Amazon DynamoDB
O modelo é baseado em uma chave e um valor.
KEY VALUE
user:123 João
session:abc123 ...
cart:456 ...
Podemos pensar em algo semelhante a:
GET user:123
Retornando:
João
Esse modelo é excelente para casos em que sabemos exatamente qual chave queremos consultar.
Aplicações comuns:
- Cache
- Sessões
- Tokens
- Carrinhos
- Rate limiting
- Dados temporários
6.3 Wide-Column
Exemplos:
- Cassandra
- HBase
Esse modelo é especialmente útil para grandes volumes de dados distribuídos.
É comum em cenários que precisam de:
- Alta disponibilidade
- Grande volume de escrita
- Distribuição geográfica
- Escalabilidade horizontal
Um exemplo seria um sistema de coleta de eventos:
USER_ID | TIMESTAMP | EVENT
--------------------------------
1 | 10:00 | LOGIN
1 | 10:05 | VIEW_PRODUCT
1 | 10:10 | PURCHASE
Esse tipo de estrutura pode ser muito eficiente para determinados padrões de acesso.
6.4 Graph Database
Exemplos:
- Neo4j
- Amazon Neptune
São bancos especializados em relações entre entidades.
Imagine uma rede social:
João
│
├── amigo de ── Maria
│
├── segue ───── Pedro
│
└── curtiu ──── Post #123
Um banco de grafos é especialmente interessante quando o problema principal está nas próprias relações.
Casos comuns:
- Redes sociais
- Sistemas de recomendação
- Detecção de fraude
- Mapas de relacionamento
- Knowledge graphs
7. SQL vs NoSQL
Uma comparação simplificada:
CaracterísticaRelacionalNão RelacionalEstruturaTabelasDocumentos, chave-valor, grafos etc.SchemaGeralmente rígidoGeralmente mais flexívelRelacionamentosExcelenteDepende do tipoJOINsSuportados nativamenteGeralmente evitados ou limitadosTransaçõesMuito fortesDepende da tecnologiaConsistênciaTradicionalmente forteDepende do banco e configuraçãoEscalabilidadeVertical + horizontalFrequentemente horizontalFlexibilidadeMenorMaiorDados altamente relacionadosExcelenteDepende do modeloGrandes volumes distribuídosPossívelFrequentemente um ponto forte
Essa tabela é apenas uma generalização.
Não significa que:
SQL = não escala
NoSQL = escala infinitamente
Isso seria uma simplificação incorreta.
Bancos relacionais modernos podem escalar horizontalmente, utilizando técnicas como:
- Replicação
- Sharding
- Particionamento
- Read replicas
- Clustering
Da mesma forma, bancos NoSQL podem oferecer transações e garantias de consistência fortes.
O importante é entender os trade-offs.
8. Escalabilidade Vertical vs Horizontal
Uma das discussões importantes em System Design é como o banco irá crescer.
Escalabilidade Vertical
Aumentamos os recursos do servidor:
ANTES
┌───────────────┐
│ DB │
│ 4 CPU │
│ 16 GB RAM │
└───────────────┘
DEPOIS
┌───────────────┐
│ DB │
│ 32 CPU │
│ 128 GB RAM │
└───────────────┘
É simples, mas existe um limite físico e financeiro.
Escalabilidade Horizontal
Adicionamos mais máquinas:
┌──────────┐
│ App │
└────┬─────┘
│
┌───────┴───────┐
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ DB 1 │ │ DB 2 │
└─────────┘ └─────────┘
Esse modelo permite distribuir carga entre vários servidores.
No entanto, também aumenta a complexidade do sistema.
9. Consistência vs Disponibilidade
Em System Design, frequentemente precisamos pensar no trade-off entre:
CONSISTÊNCIA
↕
DISPONIBILIDADE
Imagine um sistema distribuído com várias réplicas:
┌─────────┐
│ Primary │
└────┬────┘
│
┌───────┴───────┐
▼ ▼
┌─────────┐ ┌─────────┐
│ Replica │ │ Replica │
└─────────┘ └─────────┘
Se uma informação for atualizada, pode existir um pequeno intervalo até todas as réplicas receberem a alteração.
Isso pode resultar em:
Write
│
▼
Primary
│
│ replicação
▼
Replicas
Durante esse período:
Primary → valor novo
Replica → valor antigo
Esse conceito é conhecido como eventual consistency.
Para algumas aplicações, isso é perfeitamente aceitável.
Por exemplo:
Número de visualizações de um vídeo
Se o contador mostrar:
1.000.001
em um servidor e:
999.998
em outro por alguns segundos, provavelmente não existe um problema crítico.
Porém, para:
Saldo bancário
a situação é completamente diferente.
Nesse caso, precisamos de garantias de consistência muito mais fortes.
10. Como escolher entre SQL e NoSQL?
Uma forma melhor de pensar é começar pelos requisitos.
Pergunta 1 — Meus dados possuem muitos relacionamentos?
Se temos:
Users
Orders
Products
Payments
Addresses
e essas entidades estão fortemente relacionadas, um banco relacional pode ser uma excelente escolha.
Pergunta 2 — Preciso de transações complexas?
Se temos operações como:
Transferência bancária
Pagamento
Estoque
Pedido
transações fortes podem ser extremamente importantes.
Nesse cenário, bancos relacionais normalmente são uma escolha natural.
Pergunta 3 — Meu schema muda constantemente?
Se os documentos possuem estruturas muito diferentes:
Documento A
{
name,
email
}
Documento B
{
name,
phone,
address,
preferences
}
um banco orientado a documentos pode oferecer maior flexibilidade.
Pergunta 4 — Preciso escalar para um volume gigantesco?
Se o sistema precisa lidar com:
Bilhões de eventos
Milhões de usuários
Altíssima taxa de escrita
Distribuição global
um banco NoSQL distribuído pode ser interessante.
Mas isso não significa automaticamente que SQL não seja capaz de atender.
O importante é analisar:
- Volume
- Throughput
- Latência
- Padrão de leitura
- Padrão de escrita
- Consistência
- Disponibilidade
- Custo
- Complexidade operacional
11. O padrão de acesso é mais importante que o banco
Um dos principais insights para System Design é que devemos escolher o banco pensando em como os dados serão utilizados.
Imagine que temos:
10 milhões de usuários
Mas nossa aplicação sempre busca:
GET /users/{id}
Nesse cenário, um banco Key-Value pode ser extremamente eficiente.
Por outro lado, se fazemos consultas como:
Buscar todos os pedidos
de clientes de determinada cidade
que compraram determinado produto
nos últimos 30 dias
um banco relacional pode ser mais adequado.
A pergunta correta não é:
"Qual banco é mais rápido?"
A pergunta é:
"Qual banco é mais eficiente para o padrão de acesso que minha aplicação possui?"
12. Não existe obrigação de usar apenas um banco
Sistemas grandes frequentemente utilizam diferentes tecnologias.
Por exemplo:
┌─────────────┐
│ Application │
└──────┬──────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
PostgreSQL Redis Elasticsearch
│ │ │
│ │ │
Dados principais Cache Busca
Cada tecnologia possui uma responsabilidade.
PostgreSQL
Pode armazenar:
Users
Orders
Payments
Products
Redis
Pode armazenar:
Sessions
Cache
Rate Limits
Temporary Data
Elasticsearch
Pode ser utilizado para:
Full-text Search
Filtros
Busca de produtos
Esse padrão é conhecido como Polyglot Persistence.
A ideia é:
Usar a ferramenta mais adequada para cada tipo de problema.
13. Exemplo de arquitetura
Imagine um e-commerce:
CLIENT
│
▼
┌─────────────┐
│ API / APP │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
PostgreSQL Redis Elasticsearch
│ │ │
│ │ │
Dados Cache Pesquisa
críticos
Podemos definir:
PostgreSQL
├── Usuários
├── Pedidos
├── Pagamentos
└── Estoque
Redis
├── Sessões
├── Cache de produtos
└── Rate limiting
Elasticsearch
└── Busca de produtos
Nesse caso, não estamos tentando fazer um único banco resolver todos os problemas.
Cada componente possui uma função específica.
14. Insight importante: Comece simples
Um erro comum em System Design é começar pensando imediatamente em:
Kafka
Cassandra
Redis
Kubernetes
Sharding
Microservices
antes mesmo de entender o problema.
Uma arquitetura inicial pode ser:
Client
│
▼
Backend
│
▼
PostgreSQL
Isso pode ser suficiente para uma aplicação pequena ou média.
Conforme os requisitos aumentam:
PostgreSQL
│
├── Read Replicas
│
├── Partitioning
│
└── Sharding
E podemos adicionar:
Redis
para cache:
Client
│
▼
Backend
│
├──────► Redis
│
└──────► PostgreSQL
E posteriormente adicionar outras tecnologias conforme necessário.
A arquitetura deve evoluir com os requisitos.
15. Resumo dos principais insights
Banco Relacional
Use quando:
- Os dados possuem relacionamentos fortes.
- Integridade dos dados é muito importante.
- Transações complexas são necessárias.
- O schema é relativamente bem definido.
- JOINs e consultas relacionais são importantes.
- O sistema possui operações críticas que precisam de consistência.
Exemplos:
PostgreSQL
MySQL
SQL Server
Oracle
Banco Não Relacional
Pode ser interessante quando:
- O modelo de dados é mais flexível.
- O sistema precisa distribuir grandes volumes de dados.
- Existe uma necessidade específica de alta escala.
- O padrão de acesso é simples e previsível.
- O modelo de dados se encaixa melhor em documentos, chave-valor ou grafos.
Exemplos:
MongoDB → Documentos
Redis → Key-Value
Cassandra → Wide-Column
Neo4j → Grafos
16. O que responder em uma entrevista de System Design?
Uma resposta madura não seria:
"Vou usar MongoDB porque escala mais."
Nem:
"Vou usar PostgreSQL porque SQL é melhor."
Uma resposta melhor seria:
"Eu escolheria PostgreSQL porque o sistema possui entidades fortemente relacionadas, como usuários, pedidos e pagamentos, e precisamos garantir consistência e transações. Se o sistema crescer, podemos considerar replicação, particionamento e eventualmente sharding. Para dados temporários e cache, adicionaria Redis. Se a aplicação tivesse uma necessidade específica de alta escala distribuída ou um modelo de dados mais flexível, avaliaria uma solução NoSQL."
Essa abordagem demonstra que você entende trade-offs, e não apenas tecnologias.
17. Regra mental
Uma forma simples de lembrar:
PRECISA DE DADOS RELACIONADOS?
│
┌────────┴────────┐
SIM NÃO
│ │
▼ ▼
SQL? QUAL O MODELO?
│ │
│ ┌───────┼────────┐
│ │ │ │
│ Document Key-Value Graph
│ │ │ │
▼ ▼ ▼ ▼
PostgreSQL MongoDB Redis Neo4j
Mas essa decisão não deve ser feita apenas pelo modelo dos dados.
Também devemos considerar:
┌──────────────────────────┐
│ Volume de dados │
├──────────────────────────┤
│ Taxa de leitura │
├──────────────────────────┤
│ Taxa de escrita │
├──────────────────────────┤
│ Latência │
├──────────────────────────┤
│ Consistência │
├──────────────────────────┤
│ Disponibilidade │
├──────────────────────────┤
│ Escalabilidade │
├──────────────────────────┤
│ Complexidade operacional │
└──────────────────────────┘
Conclusão
A principal lição sobre bancos relacionais e não relacionais é que não existe um banco universalmente melhor.
Bancos relacionais são extremamente poderosos para sistemas que precisam de:
Relacionamentos
Integridade
Transações
Consistência
Consultas complexas
Enquanto soluções NoSQL podem ser excelentes quando precisamos de:
Flexibilidade
Alta escala distribuída
Modelos específicos de acesso
Grande volume de dados
Baixa latência
Em System Design, a escolha deve partir dos requisitos do sistema.
O banco de dados é apenas uma peça da arquitetura:
SYSTEM
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
Compute Cache Database
│ │ │
│ │ ┌─────┴─────┐
│ │ │ │
│ │ ▼ ▼
│ │ SQL NoSQL
│ │
└─────────────┴───────────────────┐
│
Trade-offs
│
▼
Escalabilidade
Consistência
Disponibilidade
Performance
Custo
A pergunta que devemos sempre fazer é:
"Quais são os requisitos do sistema e qual banco atende melhor esses requisitos?"
Essa mentalidade é muito mais importante do que decorar uma lista de bancos SQL e NoSQL.