Deep dive · Databases
DBMS: everything between your app and the disk
A Database Management System is the software that stores, protects and serves your data. This is the full map: what it does, how it works inside, the families of databases, the main products, and how to choose.
1. What a DBMS is (and what it is not)
A database is an organized collection of data. A DBMS (Database Management System, SGBD in Portuguese) is the program that manages it: it writes and reads data on disk, enforces rules, handles many users at once, recovers from crashes and answers queries. PostgreSQL is a DBMS; the data inside one PostgreSQL instance is the database.
Your application never touches data files directly. It talks to the DBMS through a language (usually SQL) over a connection, and the DBMS decides how to execute the request.
2. Why not just use files?
A JSON or CSV file works until it doesn't. A DBMS solves problems you would otherwise rebuild badly:
- Concurrency: two requests editing the same record without corrupting it.
- Durability: a power cut mid-write must not leave half-written data.
- Querying: finding one row among 50 million without reading the whole file.
- Integrity: constraints (unique email, valid foreign key) enforced in one place, not in every code path.
- Access control: who can read or change what.
- Abstraction: you say what you want; the DBMS figures out how.
3. What's inside: the architecture
A typical relational DBMS processes a query through layers:
- Connection layer: authentication, sessions, connection pooling.
- Parser: checks the SQL syntax and builds a tree.
- Planner / optimizer: picks the cheapest way to run it (which index, which join order), using statistics about the data.
- Executor: runs the chosen plan.
- Buffer cache: keeps hot pages in memory so most reads never touch the disk.
- Transaction and lock manager: keeps concurrent work consistent.
- Write-ahead log (WAL): every change is appended to a log before the data pages change, so a crash can be replayed or undone.
- Storage engine: lays data out on disk in pages.
The WAL idea is the heart of durability. Appending to a log is fast and sequential. Data pages can be written lazily later; after a crash the DBMS replays the log to rebuild a consistent state.
4. Transactions and ACID
A transaction groups several operations into one all-or-nothing unit. The classic example is a transfer: subtract from A, add to B. ACID is the set of guarantees:
| Letter | Meaning | In practice |
| Atomicity | All steps happen or none do | A failure halfway rolls everything back |
| Consistency | Data moves from one valid state to another | Constraints are never left violated |
| Isolation | Concurrent transactions don't see each other's half-done work | Controlled by isolation levels |
| Durability | Once committed, it survives a crash | Thanks to the WAL and fsync |
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- or ROLLBACK; to undo both
5. Concurrency, isolation and locking
Many users read and write at once. The DBMS has two main strategies:
- Locking: a transaction locks what it touches; others wait. Simple, but can block and cause deadlocks (two transactions waiting on each other; the DBMS detects and aborts one).
- MVCC (multi-version concurrency control): each transaction sees a snapshot; writers create new row versions instead of overwriting. Readers don't block writers. PostgreSQL, MySQL's InnoDB and Oracle use it.
The SQL standard defines isolation levels, trading safety for speed: Read Uncommitted, Read Committed, Repeatable Read, Serializable. Each level prevents more anomalies (dirty reads, non-repeatable reads, phantom reads) at a performance cost. Most DBMSs default to Read Committed (PostgreSQL) or Repeatable Read (MySQL InnoDB).
6. Indexes and the query optimizer
Without an index, finding a row means scanning the whole table (a sequential scan). An index is a separate sorted structure that lets the DBMS jump straight to matching rows, like a book's index.
- B-tree: the default; great for equality and ranges (
=, <, BETWEEN, ORDER BY).
- Hash: equality only.
- GIN / inverted: full-text search, JSON, arrays.
- GiST / R-tree: geometry and geospatial data.
- Vector (HNSW, IVFFlat): similarity search for embeddings.
Indexes cost: every write must also update them, and they take disk space. Index what you filter, join and sort on, not everything.
CREATE INDEX idx_orders_customer ON orders (customer_id);
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42;
EXPLAIN shows the plan the optimizer chose. It's the single most useful performance tool you have.
7. Storage engines: B-tree vs LSM-tree
| B-tree engines | LSM-tree engines |
| How writes work | Update pages in place | Append to memory, flush sorted files, merge in the background (compaction) |
| Strength | Fast, predictable reads | Very high write throughput |
| Cost | Random I/O on writes | Read and write amplification from compaction |
| Examples | PostgreSQL, MySQL InnoDB, SQLite | RocksDB, Cassandra, ScyllaDB, LevelDB |
Also: row-oriented storage keeps whole rows together (good for transactions, OLTP); column-oriented storage keeps each column together (good for analytics scans, OLAP).
8. The families of DBMS
Relational (RDBMS)
Tables, rows, columns, SQL, joins, strict schemas. Best default for most applications. PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, Oracle.
Document
Store JSON-like documents; flexible schema; related data nested together. MongoDB, Couchbase, Firestore. Good when records are self-contained and shape varies; weaker at many-to-many relations.
Key-value
A giant dictionary: get/set by key. Extremely fast. Redis, Valkey, DynamoDB, Memcached. Used for caches, sessions, queues, counters.
Wide-column
Rows grouped by partition key with flexible columns, built for huge write volume across many nodes. Cassandra, ScyllaDB, HBase, Bigtable.
Graph
Nodes and edges as first-class citizens; fast multi-hop relationship queries (recommendations, fraud, social graphs). Neo4j, Amazon Neptune.
Time-series
Optimized for timestamped data streams (metrics, IoT). TimescaleDB, InfluxDB, Prometheus' storage.
Search engines
Inverted indexes for full-text relevance. Elasticsearch, OpenSearch, Meilisearch, Typesense. Usually a secondary store fed from the main database.
Vector
Store embeddings and find nearest neighbors, the backbone of RAG for LLM apps. pgvector (inside PostgreSQL), Pinecone, Qdrant, Weaviate, Milvus.
Analytical (OLAP) warehouses
Column-oriented systems for big aggregations over historical data. BigQuery, Snowflake, ClickHouse, DuckDB, Redshift.
NewSQL / distributed SQL
SQL and ACID, but horizontally scalable across nodes. CockroachDB, YugabyteDB, Google Spanner, TiDB.
9. The main products compared
| DBMS | Type | Best at | Watch out for |
| PostgreSQL | Relational | General purpose, rich SQL, JSONB, extensions (pgvector, PostGIS) | Needs tuning and vacuuming at large scale |
| MySQL / MariaDB | Relational | Web apps, huge ecosystem, simple replication | Historically fewer advanced SQL features |
| SQLite | Embedded relational | Local apps, tests, small sites; zero config, single file | One writer at a time; not for heavy concurrent writes |
| SQL Server | Relational | Microsoft shops, BI tooling | Licensing cost |
| Oracle | Relational | Large enterprises, mature features | Cost and complexity |
| MongoDB | Document | Flexible schemas, nested data | Joins and cross-document consistency need care |
| Redis / Valkey | Key-value (in-memory) | Caching, sessions, rate limits, queues | Memory-bound; not a primary store by default |
| DynamoDB | Key-value / wide | Serverless scale, predictable latency | You must design around access patterns up front |
| Cassandra | Wide-column | Massive writes, multi-region | Limited queries; operational complexity |
| ClickHouse / DuckDB | Analytical columnar | Fast aggregations over huge datasets | Not for row-by-row transactional updates |
10. Scaling: replication, sharding, CAP
- Vertical scaling: a bigger machine. Simplest; has a ceiling.
- Read replicas: copies that serve reads; the primary takes writes. Replication can be synchronous (safe, slower) or asynchronous (fast, replicas may lag).
- Sharding (partitioning): split data across nodes by a key. Scales writes, but cross-shard queries and transactions get hard.
- Caching layer: put Redis in front for hot reads.
The CAP theorem: when the network partitions, a distributed system must choose between consistency (every read sees the latest write) and availability (every request gets an answer). Many NoSQL systems choose eventual consistency; distributed SQL systems usually choose consistency. The related BASE idea (basically available, soft state, eventually consistent) describes the looser model.
11. Operations: backup, security, migrations
- Backups: logical dumps (
pg_dump) for portability; physical/continuous archiving + point-in-time recovery for production. A backup you never restored is not a backup.
- Security: least-privilege roles, TLS in transit, encryption at rest, never expose the database port publicly, always use parameterized queries to prevent SQL injection.
- Migrations: schema changes as versioned files (Prisma, Drizzle, Flyway, Alembic). Make them backward compatible so deploys don't break running code.
- Connection pooling: databases handle a limited number of connections; use PgBouncer or the provider's pooler, especially with serverless functions.
- Monitoring: slow query log,
EXPLAIN, cache hit ratio, replication lag, disk space.
- Managed vs self-hosted: managed services (Supabase, Neon, RDS, PlanetScale-style offerings) trade money for not running backups, failover and upgrades yourself.
12. How to choose
Default answer: start with PostgreSQL. It handles relational data, JSON, full-text search and vectors, so it covers most apps for a long time. Add specialized stores only when a measured need appears.
| If you need… | Reach for… |
| Business data with relationships and transactions | PostgreSQL or MySQL |
| A tiny app, prototype, local or edge storage | SQLite |
| Caching, sessions, rate limiting | Redis / Valkey |
| Flexible, document-shaped data | MongoDB (or PostgreSQL JSONB) |
| Analytics over millions or billions of rows | ClickHouse, BigQuery, DuckDB |
| Semantic search for an AI app | pgvector first; a dedicated vector DB at large scale |
| Full-text search with ranking | PostgreSQL FTS first; Meilisearch/Elasticsearch if it grows |
| Global scale with SQL guarantees | CockroachDB, Spanner, YugabyteDB |
Questions to ask: What are the access patterns? How strict must consistency be? How big will it get? Who operates it? What does the team already know? The last one matters more than most benchmarks.
13. Common mistakes
- Choosing NoSQL "to scale" before having a scaling problem, then reinventing joins in application code.
- No indexes on foreign keys and filter columns, then blaming the database.
SELECT * and N+1 queries (one query per item in a loop).
- Skipping transactions for multi-step writes.
- Using the database as a queue or cache when a purpose-built tool is simpler.
- Never testing restores; running with the default superuser; exposing the database to the internet.
- Treating SQLite as a toy or Postgres as magic. Know each one's limits.
Imersão · Bancos de dados
SGBDs: tudo o que fica entre seu app e o disco
Um Sistema de Gerenciamento de Banco de Dados é o software que armazena, protege e serve seus dados. Este é o mapa completo: o que ele faz, como funciona por dentro, as famílias de bancos, os principais produtos e como escolher.
1. O que é um SGBD (e o que não é)
Um banco de dados é uma coleção organizada de dados. Um SGBD (Sistema de Gerenciamento de Banco de Dados, DBMS em inglês) é o programa que o gerencia: grava e lê dados no disco, aplica regras, atende muitos usuários ao mesmo tempo, se recupera de falhas e responde queries. O PostgreSQL é um SGBD; os dados dentro de uma instância dele são o banco de dados.
Sua aplicação nunca mexe nos arquivos de dados diretamente. Ela conversa com o SGBD por uma linguagem (normalmente SQL) através de uma conexão, e o SGBD decide como executar o pedido.
2. Por que não usar só arquivos?
Um arquivo JSON ou CSV funciona até não funcionar mais. Um SGBD resolve problemas que você acabaria reconstruindo mal:
- Concorrência: duas requisições editando o mesmo registro sem corromper.
- Durabilidade: uma queda de energia no meio de uma escrita não pode deixar dados pela metade.
- Consulta: achar uma linha entre 50 milhões sem ler o arquivo inteiro.
- Integridade: restrições (e-mail único, chave estrangeira válida) aplicadas num só lugar, não em cada trecho de código.
- Controle de acesso: quem pode ler ou alterar o quê.
- Abstração: você diz o que quer; o SGBD descobre como.
3. O que tem por dentro: a arquitetura
Um SGBD relacional típico processa uma query em camadas:
- Camada de conexão: autenticação, sessões, pool de conexões.
- Parser: valida a sintaxe SQL e monta uma árvore.
- Planner / otimizador: escolhe o jeito mais barato de executar (qual índice, qual ordem de join), usando estatísticas dos dados.
- Executor: roda o plano escolhido.
- Buffer cache: mantém páginas quentes em memória para que a maioria das leituras nem toque o disco.
- Gerenciador de transações e locks: mantém o trabalho concorrente consistente.
- Write-ahead log (WAL): toda mudança é anexada a um log antes de alterar as páginas de dados, então uma queda pode ser refeita ou desfeita.
- Storage engine: organiza os dados em páginas no disco.
A ideia do WAL é o coração da durabilidade. Anexar a um log é rápido e sequencial. As páginas de dados podem ser gravadas depois, sem pressa; após uma queda o SGBD reaplica o log para reconstruir um estado consistente.
4. Transações e ACID
Uma transação agrupa várias operações numa unidade tudo-ou-nada. O exemplo clássico é uma transferência: tira de A, soma em B. ACID é o conjunto de garantias:
| Letra | Significado | Na prática |
| Atomicidade | Todos os passos acontecem ou nenhum | Uma falha no meio desfaz tudo |
| Consistência | Os dados passam de um estado válido a outro | Restrições nunca ficam violadas |
| Isolamento | Transações concorrentes não enxergam o trabalho incompleto umas das outras | Controlado por níveis de isolamento |
| Durabilidade | Depois do commit, sobrevive a uma queda | Graças ao WAL e ao fsync |
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- ou ROLLBACK; para desfazer as duas
5. Concorrência, isolamento e locks
Muitos usuários leem e escrevem ao mesmo tempo. O SGBD tem duas estratégias principais:
- Locking: a transação trava o que toca; as outras esperam. Simples, mas pode bloquear e causar deadlocks (duas transações esperando uma pela outra; o SGBD detecta e aborta uma).
- MVCC (controle de concorrência multiversão): cada transação enxerga um snapshot; escritas criam novas versões da linha em vez de sobrescrever. Leitores não bloqueiam escritores. PostgreSQL, InnoDB do MySQL e Oracle usam.
O padrão SQL define níveis de isolamento, trocando segurança por velocidade: Read Uncommitted, Read Committed, Repeatable Read, Serializable. Cada nível previne mais anomalias (dirty read, non-repeatable read, phantom read) com custo de desempenho. A maioria dos SGBDs usa Read Committed (PostgreSQL) ou Repeatable Read (InnoDB do MySQL) como padrão.
6. Índices e o otimizador de queries
Sem índice, achar uma linha significa varrer a tabela inteira (sequential scan). Um índice é uma estrutura ordenada separada que deixa o SGBD ir direto às linhas certas, como o índice de um livro.
- B-tree: o padrão; ótimo para igualdade e intervalos (
=, <, BETWEEN, ORDER BY).
- Hash: só igualdade.
- GIN / invertido: busca textual, JSON, arrays.
- GiST / R-tree: geometria e dados geoespaciais.
- Vetorial (HNSW, IVFFlat): busca por similaridade de embeddings.
Índices têm custo: toda escrita também precisa atualizá-los, e ocupam disco. Indexe o que você filtra, junta e ordena, não tudo.
CREATE INDEX idx_orders_customer ON orders (customer_id);
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42;
O EXPLAIN mostra o plano escolhido pelo otimizador. É a ferramenta de performance mais útil que você tem.
7. Engines de armazenamento: B-tree vs LSM-tree
| Engines B-tree | Engines LSM-tree |
| Como escrevem | Atualizam páginas no lugar | Anexam em memória, descarregam arquivos ordenados, juntam em segundo plano (compaction) |
| Ponto forte | Leituras rápidas e previsíveis | Throughput de escrita muito alto |
| Custo | I/O aleatório nas escritas | Amplificação de leitura e escrita pela compaction |
| Exemplos | PostgreSQL, MySQL InnoDB, SQLite | RocksDB, Cassandra, ScyllaDB, LevelDB |
Também: o armazenamento orientado a linhas guarda a linha inteira junta (bom para transações, OLTP); o orientado a colunas guarda cada coluna junta (bom para varreduras analíticas, OLAP).
8. As famílias de SGBD
Relacional (SGBDR)
Tabelas, linhas, colunas, SQL, joins, schema rígido. Melhor padrão para a maioria das aplicações. PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, Oracle.
Documento
Guarda documentos tipo JSON; schema flexível; dados relacionados aninhados. MongoDB, Couchbase, Firestore. Bom quando os registros são autocontidos e o formato varia; mais fraco em relações muitos-para-muitos.
Chave-valor
Um dicionário gigante: get/set por chave. Extremamente rápido. Redis, Valkey, DynamoDB, Memcached. Usado em cache, sessões, filas, contadores.
Colunas largas
Linhas agrupadas por chave de partição com colunas flexíveis, feitas para volume enorme de escrita em muitos nós. Cassandra, ScyllaDB, HBase, Bigtable.
Grafo
Nós e arestas como cidadãos de primeira classe; consultas rápidas de relacionamentos em vários saltos (recomendação, fraude, redes sociais). Neo4j, Amazon Neptune.
Séries temporais
Otimizados para fluxos de dados com timestamp (métricas, IoT). TimescaleDB, InfluxDB, armazenamento do Prometheus.
Motores de busca
Índices invertidos para relevância em texto. Elasticsearch, OpenSearch, Meilisearch, Typesense. Normalmente um armazenamento secundário alimentado pelo banco principal.
Vetoriais
Guardam embeddings e acham os vizinhos mais próximos, base do RAG em apps com LLM. pgvector (dentro do PostgreSQL), Pinecone, Qdrant, Weaviate, Milvus.
Data warehouses analíticos (OLAP)
Sistemas colunares para grandes agregações sobre dados históricos. BigQuery, Snowflake, ClickHouse, DuckDB, Redshift.
NewSQL / SQL distribuído
SQL e ACID, mas escalando horizontalmente entre nós. CockroachDB, YugabyteDB, Google Spanner, TiDB.
9. Os principais produtos comparados
| SGBD | Tipo | Melhor em | Atenção |
| PostgreSQL | Relacional | Uso geral, SQL rico, JSONB, extensões (pgvector, PostGIS) | Pede ajuste e vacuum em grande escala |
| MySQL / MariaDB | Relacional | Apps web, ecossistema enorme, replicação simples | Historicamente menos recursos SQL avançados |
| SQLite | Relacional embarcado | Apps locais, testes, sites pequenos; zero configuração, um arquivo | Um escritor por vez; não serve para muita escrita concorrente |
| SQL Server | Relacional | Ambientes Microsoft, ferramentas de BI | Custo de licença |
| Oracle | Relacional | Grandes empresas, recursos maduros | Custo e complexidade |
| MongoDB | Documento | Schemas flexíveis, dados aninhados | Joins e consistência entre documentos exigem cuidado |
| Redis / Valkey | Chave-valor (em memória) | Cache, sessões, rate limit, filas | Limitado pela memória; não é banco principal por padrão |
| DynamoDB | Chave-valor / colunas largas | Escala serverless, latência previsível | É preciso modelar pelos padrões de acesso desde o início |
| Cassandra | Colunas largas | Escrita massiva, multi-região | Consultas limitadas; complexidade operacional |
| ClickHouse / DuckDB | Colunar analítico | Agregações rápidas sobre datasets enormes | Não serve para updates transacionais linha a linha |
10. Escala: replicação, sharding, CAP
- Escala vertical: uma máquina maior. A mais simples; tem teto.
- Read replicas: cópias que atendem leituras; o primário recebe as escritas. A replicação pode ser síncrona (segura, mais lenta) ou assíncrona (rápida, réplicas podem atrasar).
- Sharding (particionamento): divide os dados entre nós por uma chave. Escala escritas, mas consultas e transações entre shards ficam difíceis.
- Camada de cache: Redis na frente para leituras quentes.
O teorema CAP: quando a rede particiona, um sistema distribuído precisa escolher entre consistência (toda leitura vê a última escrita) e disponibilidade (toda requisição recebe resposta). Muitos NoSQL escolhem consistência eventual; sistemas SQL distribuídos geralmente escolhem consistência. A ideia relacionada de BASE (basically available, soft state, eventually consistent) descreve o modelo mais frouxo.
11. Operação: backup, segurança, migrations
- Backups: dumps lógicos (
pg_dump) pela portabilidade; arquivamento contínuo físico + recuperação point-in-time em produção. Um backup que você nunca restaurou não é backup.
- Segurança: roles com privilégio mínimo, TLS em trânsito, criptografia em repouso, nunca expor a porta do banco publicamente, sempre usar queries parametrizadas contra SQL injection.
- Migrations: mudanças de schema como arquivos versionados (Prisma, Drizzle, Flyway, Alembic). Faça-as retrocompatíveis para que o deploy não quebre o código em execução.
- Connection pooling: bancos aguentam um número limitado de conexões; use PgBouncer ou o pooler do provedor, principalmente com funções serverless.
- Monitoramento: slow query log,
EXPLAIN, cache hit ratio, atraso de replicação, espaço em disco.
- Gerenciado vs auto-hospedado: serviços gerenciados (Supabase, Neon, RDS e similares) trocam dinheiro por não ter que cuidar de backup, failover e upgrades.
12. Como escolher
Resposta padrão: comece com PostgreSQL. Ele cobre dados relacionais, JSON, busca textual e vetores, o que atende a maioria dos apps por bastante tempo. Adicione armazenamentos especializados só quando surgir uma necessidade medida.
| Se você precisa de… | Use… |
| Dados de negócio com relações e transações | PostgreSQL ou MySQL |
| App minúsculo, protótipo, armazenamento local ou na borda | SQLite |
| Cache, sessões, rate limiting | Redis / Valkey |
| Dados flexíveis em formato de documento | MongoDB (ou JSONB do PostgreSQL) |
| Analytics sobre milhões ou bilhões de linhas | ClickHouse, BigQuery, DuckDB |
| Busca semântica para um app de IA | pgvector primeiro; banco vetorial dedicado em grande escala |
| Busca textual com ranking | FTS do PostgreSQL primeiro; Meilisearch/Elasticsearch se crescer |
| Escala global com garantias SQL | CockroachDB, Spanner, YugabyteDB |
Perguntas a fazer: quais são os padrões de acesso? Quão estrita precisa ser a consistência? Quão grande vai ficar? Quem vai operar? O que o time já conhece? A última pesa mais que a maioria dos benchmarks.
13. Erros comuns
- Escolher NoSQL "para escalar" antes de ter problema de escala, e reinventar joins no código da aplicação.
- Sem índice em chaves estrangeiras e colunas de filtro, e depois culpar o banco.
SELECT * e queries N+1 (uma query por item num loop).
- Não usar transações em escritas de vários passos.
- Usar o banco como fila ou cache quando uma ferramenta específica é mais simples.
- Nunca testar restore; rodar com o superusuário padrão; expor o banco na internet.
- Tratar o SQLite como brinquedo ou o Postgres como mágica. Conheça os limites de cada um.