SQLite: quando usar esse banco de dados (e quando não usar)
SQLite não é brinquedo: está em um trilhão de dispositivos. Entenda quando ele ganha de Postgres — e onde os limites reais de concorrência aparecem.
A reunião de arquitetura durou duas horas. O time debateu se ia usar PostgreSQL ou MySQL. Alguém sugeriu Aurora Serverless. Outro mencionou CockroachDB "só pra ter na mesa". Ninguém disse SQLite porque parecia amador demais. O projeto era um script de ETL que rodava uma vez por dia e precisava cruzar dois arquivos CSV. SQLite teria resolvido em dez minutos.
Esse post não é sobre quando SQLite é suficiente. É sobre quando SQLite é a escolha certa — e sobre os casos onde você vai se arrepender se ignorar os limites reais da ferramenta.
SQLite não é um banco "de brinquedo"
É o banco de dados mais implantado do mundo. Está no seu iPhone, no seu Android, no Firefox, no Chrome, no Slack desktop, no VSCode, no Photoshop. A estimativa é de mais de um trilhão de instâncias ativas. A documentação oficial recomenda explicitamente seu uso em produção — com ressalvas claras sobre quando não usar.
A diferença fundamental em relação a Postgres ou MySQL é arquitetural: SQLite não tem servidor. Não tem processo separado, não tem conexão TCP, não tem autenticação de rede. É uma biblioteca que lê e escreve direto num arquivo. Sua aplicação linka a biblioteca, abre o arquivo, e faz queries SQL normais.
import sqlite3
conn = sqlite3.connect("meu_banco.db")
cursor = conn.execute("SELECT * FROM pedidos WHERE status = 'pendente'")
rows = cursor.fetchall()
Isso não é uma limitação disfarçada. É um design intencional com vantagens reais em vários contextos.
Onde SQLite ganha de Postgres sem discussão
Aplicações desktop e mobile
Qualquer app que precisa de persistência local e roda num dispositivo controlado — desktop, mobile, terminal — SQLite é a escolha natural. Não há servidor para configurar, não há porta para abrir, não há serviço para manter rodando. O banco é o arquivo.
O VSCode guarda extensões, histórico e estado em SQLite. O iOS usa SQLite para contatos, calendário e mensagens. Não porque a Apple não tem infraestrutura para rodar Postgres — mas porque, para dados locais de um dispositivo, arquivo SQLite é estritamente melhor.
Testes de integração
Criar um banco Postgres por suite de testes exige Docker, ou um servidor rodando, ou setup de CI com service containers. Com SQLite em memória, é uma linha:
conn = sqlite3.connect(":memory:")
O banco existe, aceita o schema completo, roda as queries, e some quando o processo termina. Zero I/O de disco, zero estado compartilhado entre testes, zero race condition de limpeza.
Para a maioria dos casos onde você está testando lógica de repositório — não a compatibilidade com dialeto específico de Postgres — SQLite em memória é mais rápido, mais simples e mais isolado do que qualquer alternativa.
Edge computing e CDN workers
O ciclo mais recente do SQLite tem sido no edge. Cloudflare D1, Turso e Fly.io LiteFS colocaram SQLite em produção distribuída — com replicas lidas em múltiplas regiões e escrita centralizada. A lógica é simples: se o banco cabe num arquivo, você pode replicá-lo geograficamente sem a complexidade de um cluster Postgres.
O Turso mede latência de leitura em microsegundos em replicas locais. Para workloads de leitura intensiva com escrita centralizada, é uma arquitetura que faz mais sentido do que rotear cada leitura para uma instância Postgres em us-east-1.
Ferramentas CLI e scripts de processamento
Se você está escrevendo uma ferramenta de linha de comando que precisa de persistência — cache de resultados, histórico de execuções, índice local — SQLite é o formato certo. É mais estruturado que JSON, mais consultável que CSV, mais leve que um servidor.
# SQLite como formato de dados processável
sqlite3 dados.db "SELECT cidade, COUNT(*) FROM clientes GROUP BY cidade ORDER BY 2 DESC"
Você pode distribuir o arquivo .db como artefato, abrir no DB Browser for SQLite, consultar com qualquer linguagem. É um formato de dados com SQL por cima.
Os limites reais — onde não usar
SQLite é explícito nos seus anti-casos de uso. A documentação lista:
Alta concorrência de escrita. SQLite usa file-level locking. Quando um processo escreve, todos os outros que tentam escrever ficam bloqueados. Com WAL mode (Write-Ahead Logging), leituras não bloqueiam escritas — mas escritas ainda se serializam. Se você tem dezenas de workers escrevendo simultaneamente, vai acumular contenção e timeouts.
# Isso pode funcionar com WAL mode
conn = sqlite3.connect("banco.db")
conn.execute("PRAGMA journal_mode=WAL")
# Mas dez workers escrevendo ao mesmo tempo ainda vão se serializar
Dados que pertencem à rede, não ao disco local. Se o banco precisa ser acessado por múltiplos servidores simultaneamente, SQLite num filesystem de rede (NFS, EFS, SMB) é receita para corrupção. File locking sobre rede não é confiável. Nesse cenário, você precisa de um banco com servidor — Postgres, MySQL, o que for.
Tipos e validações avançadas. SQLite tem um sistema de tipos permissivo por design. Você pode inserir texto numa coluna INTEGER sem erro. Para sistemas que dependem de restrições de tipo rigorosas, check constraints complexas ou tipos customizados (como JSONB, UUID, INET do Postgres), vai sentir a diferença.
Alto volume de escrita concorrente num serviço web. Um servidor web que recebe mil requests simultâneos escrevendo no mesmo SQLite vai ter problemas. Não é hipotético — é o limite documentado. Para workloads assim, Postgres existe por boas razões.
WAL mode: o que muda na prática
O modo padrão do SQLite usa rollback journal. WAL (Write-Ahead Logging) é uma alternativa que melhora significativamente a performance em leitura concorrente:
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA synchronous=NORMAL") # tradeoff: mais rápido, menos durável
Com WAL, leitores não bloqueiam o escritor e o escritor não bloqueia leitores. A serialização ainda existe entre escritores, mas o throughput total melhora muito para workloads de leitura intensiva.
Para aplicações desktop e scripts, habilitar WAL mode e ajustar cache_size já resolve a maioria das queixas de performance com SQLite.
SQLite em produção: o critério real
A pergunta certa não é "é grande o suficiente para merecer Postgres?" — é "o banco é local à aplicação ou precisa ser acessado pela rede?".
Se o banco vive no mesmo processo ou na mesma máquina da aplicação, e as escritas não são altamente concorrentes, SQLite é uma escolha legítima de produção. O SQLite tem uptime de décadas, zero dependências externas, backup trivial (cópia do arquivo), e uma API SQL completa.
Se o banco precisa ser compartilhado entre múltiplos servidores, ou se o volume de escrita concorrente é alto, use Postgres ou MySQL. Não porque SQLite seja inferior — mas porque esses são os casos que ele explicitamente não foi projetado para resolver.
Esse critério é diferente da discussão de SQL versus NoSQL — se quiser entender quando um banco relacional faz sentido em relação a alternativas de documento ou chave-valor, escrevi sobre isso em banco relacional vs NoSQL.
Queries SQL independente do banco
Algo que o SQLite deixa claro: SQL não pertence a nenhum banco específico. A maioria das queries que você escreve para Postgres funciona em SQLite com pequenos ajustes — e vice-versa. Antes de rodar qualquer query em produção, eu formato e reviso no Formatador SQL, que suporta múltiplos dialetos e deixa a lógica legível antes de qualquer coisa.
Perguntas frequentes
SQLite aguenta produção ou é só para desenvolvimento?
Aguenta — com condições. A documentação oficial do SQLite lista casos de uso de produção explicitamente: aplicações desktop, mobile, IoT, sites de baixo a médio tráfego (menos de ~100k requests/dia na maioria dos casos), e qualquer sistema onde o banco é local à aplicação. O critério é concorrência de escrita, não tamanho do projeto.
Qual o limite de tamanho de banco SQLite?
O limite teórico é 281 TB. Na prática, bancos SQLite de centenas de gigabytes funcionam bem para leitura. O gargalo real não é tamanho — é concorrência de escrita. Um banco de 50GB com leitura intensiva e escritas ocasionais vai performar melhor em SQLite do que um banco de 5GB com 200 escritas concorrentes por segundo.
SQLite suporta transações ACID?
Sim, completamente. Atomicidade, consistência, isolamento e durabilidade estão implementados. O SQLite foi projetado para ser correto antes de ser rápido — as propriedades ACID são garantidas mesmo em caso de crash de processo ou queda de energia (com synchronous=FULL, que é o padrão).
Como faço backup de SQLite em produção?
# Cópia online sem travar o banco (SQLite 3.27+)
sqlite3 producao.db ".backup backup_$(date +%Y%m%d).db"
# Ou com a API de backup do SQLite no código
O backup é uma cópia de arquivo. Para sistemas críticos em produção, automatize com WAL mode e backup online — não precisa parar a aplicação.
SQLite não é escolha de quem não conhece as opções
É escolha de quem conhece o suficiente para não usar uma bazuca onde uma faca resolve.
A tendência de inflar a infraestrutura porque "um dia vai precisar de escala" cria sistemas mais complexos, mais caros e mais difíceis de operar — sem nenhum benefício real enquanto o volume não justifica. Para scripts, ferramentas CLI, apps desktop, testes, edge workers e sistemas com escrita baixa e leitura alta, SQLite é a ferramenta certa.
Para um serviço web com múltiplos servidores e escrita concorrente, não é. Esse tradeoff não é uma opinião — está na documentação oficial, e quem ignora vai encontrar o limite na pior hora possível.
- 01 O que é JSON e como esse formato funciona Entenda o que é JSON, seus seis tipos de dados, sintaxe obrigatória e onde o formato é usado — com exemplos reais em JavaScript, Python e Go.
- 02 Como os LLMs geram respostas: tokens, predição e sampling explicados Tokenização, predição autorregressiva, temperatura e Top-P: a mecânica interna de como modelos de linguagem transformam um prompt em texto.