Back

O Renascimento do SQLite: Por que o banco de dados mais usado do mundo está dominando a produção em 2026

Um dado que talvez te surpreenda: SQLite é o motor de banco de dados mais implantado do mundo. Não é PostgreSQL. Não é MySQL. Não é MongoDB. É SQLite. Tá em cada iPhone, cada Android, cada Mac, cada máquina Windows 10+, cada browser Firefox e Chrome, cada Airbus A350, e até nos rovers de Marte. Existem literalmente trilhões de bancos de dados SQLite ativos agora.

E mesmo assim, por anos a comunidade dev tratou como brinquedo. "SQLite não serve pra produção." "Pra protótipo tá ok." "Uma hora você vai precisar de um banco de dados de verdade." Essa mentalidade era tão enraizada que sugerir SQLite pra qualquer coisa além de dev local era pedir pra ser zoado no code review.

Isso mudou. Em 2025-2026 rolou algo notável. Uma constelação de tecnologias — Turso, Cloudflare D1, LibSQL, Litestream e o movimento de bancos embarcados — convergiram pra resolver as limitações reais do SQLite preservando sua simplicidade absurda. O resultado? SQLite agora é um banco de produção legítimo pra uma classe de aplicações que antes era impensável.

Não é hype. É arquitetura. Vamos destrinchar exatamente o que tá acontecendo, por que importa, e quando você deve (e não deve) escolher SQLite em 2026.

Por que SQLite era "inadequado pra produção"

Antes de se empolgar, vamos ser honestos sobre por que todo mundo descartava SQLite no servidor. Não estavam errados — eram avaliações corretas de outra era.

As limitações clássicas

1. Concorrência single-writer. SQLite usa lock a nível de arquivo. Um escritor por vez. No modo WAL (Write-Ahead Logging), você tem leituras concorrentes com um escritor, mas só isso. Se a app precisa de throughput de escrita alto com múltiplas conexões, o SQLite tradicional engasga.

2. Sem replicação nativa. PostgreSQL tem streaming replication. MySQL tem binlog replication. SQLite tem... copiar o arquivo. Pra qualquer app que precise de alta disponibilidade, failover ou distribuição geográfica, SQLite não era opção.

3. Storage de máquina única. O banco é um arquivo no disco. Sem clustering, sem sharding, sem storage distribuído. Seus dados vivem e morrem com aquela máquina.

4. Sem acesso por rede. Diferente do PostgreSQL ou MySQL que rodam como serviços de rede aceitando conexões de qualquer lugar, SQLite é uma biblioteca embarcada. A aplicação fala diretamente com ele — sem acesso compartilhado de múltiplos serviços.

São limitações reais. Não sumiram. Mas o cenário ao redor mudou tão drasticamente que agora importam muito menos — ou foram diretamente resolvidas.

O que mudou: Os quatro pilares

Pilar 1: O hardware ficou rápido o suficiente

A mudança mais subestimada é hardware. SSDs NVMe modernos entregam ~500K-1M IOPS aleatórios com latência sub-100μs. Quando SQLite roda em NVMe, o modelo single-writer consegue tratar milhares de escritas por segundo — de sobra pra grande maioria das apps web.

Olha os números:

HDD Tradicional:
  IOPS aleatórios: ~100-200
  Latência de escrita: ~5-10ms
  Throughput SQLite: ~100-200 writes/sec

NVMe SSD (2026):
  IOPS aleatórios: ~500K-1M
  Latência de escrita: ~10-50μs
  Throughput SQLite: ~10K-50K writes/sec

A maioria das apps web não precisa de mais que algumas centenas de escritas por segundo. Um e-commerce processando 100 pedidos por minuto? Menos de 2 escritas por segundo. Uma app SaaS com 10K usuários ativos diários? Difícil passar de 100 writes/sec. SQLite em NVMe resolve isso tranquilamente.

Pilar 2: Modo WAL e BEGIN CONCURRENT

O modo WAL do SQLite, que existe desde 2010, permite leituras concorrentes durante a escrita. A maioria dos devs nunca fez benchmark disso — ouviu "single writer" e seguiu em frente.

Na prática, WAL entrega throughput excelente:

-- Ativar modo WAL (faz uma vez só) PRAGMA journal_mode=WAL; -- Configuração SQLite recomendada pra produção PRAGMA busy_timeout = 5000; -- Esperar até 5s por locks PRAGMA synchronous = NORMAL; -- Durabilidade suficiente, muito mais rápido PRAGMA cache_size = -64000; -- 64MB de cache PRAGMA foreign_keys = ON; -- Integridade referencial PRAGMA temp_store = MEMORY; -- Tabelas temporárias em RAM

A extensão BEGIN CONCURRENT (disponível em forks como LibSQL) vai além, permitindo múltiplos escritores simultâneos desde que não modifiquem as mesmas páginas:

BEGIN CONCURRENT; INSERT INTO orders (user_id, total) VALUES (42, 99.99); COMMIT; -- Múltiplas conexões podem executar isso ao mesmo tempo -- Conflitos só acontecem se tocarem as mesmas páginas de dados

Pilar 3: LibSQL — O fork que mudou tudo

LibSQL é um fork open-source do SQLite que mantém compatibilidade total enquanto adiciona as funcionalidades que todo mundo queria. Criado pelo time do Turso, LibSQL trata as limitações do SQLite não como leis da física, mas como problemas de engenharia a resolver.

Funcionalidades-chave:

  • Replicação nativa. LibSQL suporta réplicas embarcadas que sincronizam de um servidor primário. Performance de leitura local com durabilidade replicada.
  • BEGIN CONCURRENT pra suporte multi-writer.
  • Modo servidor. LibSQL roda como servidor acessível por rede (via HTTP e WebSockets), resolvendo a limitação de "sem acesso por rede".
  • Melhorias em ALTER TABLE. Operações dolorosas no SQLite (como adicionar colunas com constraints) ficam mais suaves.
  • WASM UDFs. Funções definidas pelo usuário escritas em WebAssembly.
  • Busca vetorial embutida pra casos de uso de AI/embeddings.

Pilar 4: A camada de plataformas

A revolução real não é o LibSQL sozinho — são as plataformas construídas em cima dele.

Turso é o serviço gerenciado de LibSQL que oferece:

  • Réplicas edge em 30+ localidades mundiais
  • Réplicas embarcadas que sincronizam com sua aplicação (leituras sub-milissegundo)
  • Bancos por tenant (um banco por usuário/organização — sem hacks multi-tenant)
  • Recuperação point-in-time e branching

Cloudflare D1 é a oferta SQLite serverless da Cloudflare:

  • Roda na rede edge da Cloudflare (300+ cidades)
  • Integração nativa com Workers (zero salto de rede)
  • Replicação de leitura automática pra nós edge
  • API SQL com operações em batch

LiteFS (Fly.io) vale mencionar pelo contexto histórico:

  • Replicação SQLite baseada em FUSE entre máquinas do Fly.io
  • Eleição automática de primário e failover
  • Transparente pra aplicação
  • ⚠️ Atenção: LiteFS Cloud (o serviço de backup gerenciado) foi descontinuado em outubro de 2024, e a Fly.io reduziu a prioridade do desenvolvimento ativo. O LiteFS continua estável e usável em produção, mas tá em estado pre-1.0 beta sem roadmap garantido. Pra projetos novos, Turso ou D1 são apostas mais seguras.

Litestream:

  • Backup contínuo por streaming de SQLite pra S3/GCS/Azure
  • Recuperação point-in-time de object storage
  • Leve, testado em batalha e ativamente mantido — escolha sólida pra backup self-managed de SQLite

A arquitetura: Como SQLite em produção realmente funciona

Vamos ao concreto. É assim que você arquiteta uma aplicação real com SQLite em 2026.

Padrão 1: Turso com réplicas embarcadas

O padrão mais popular pra apps web. Sua aplicação embarca uma réplica SQLite local que sincroniza de um primário Turso.

import { createClient } from '@libsql/client'; // Criar cliente com réplica embarcada const db = createClient({ url: 'file:local.db', // Réplica local syncUrl: 'libsql://my-app-db.turso.io', // Primário remoto authToken: process.env.TURSO_AUTH_TOKEN, syncInterval: 60, // Sincronizar a cada 60s }); // Leituras batem na réplica local — sub-milissegundo const users = await db.execute('SELECT * FROM users WHERE active = 1'); // Escritas vão pro primário remoto, depois sincronizam de volta await db.execute({ sql: 'INSERT INTO users (email, name) VALUES (?, ?)', args: ['[email protected]', 'Alice'], }); // Forçar sincronização quando precisar await db.sync();

O caminho de leitura é extraordinário: sua aplicação lê de um arquivo SQLite local. Sem salto de rede. Sem pool de conexões. Sem cold start. Latência sub-milissegundo pra cada leitura. Escritas vão pro primário pela rede, mas tudo bem — a maioria das apps é read-heavy.

Padrão 2: Banco por tenant com Turso

Onde a arquitetura do SQLite brilha mais. Em vez de um banco com coluna tenant_id em cada tabela, cada tenant ganha seu próprio banco:

import { createClient } from '@libsql/client'; function getClientForTenant(tenantId: string) { return createClient({ url: `libsql://${tenantId}-my-app.turso.io`, authToken: process.env.TURSO_AUTH_TOKEN, }); } // Operações do Tenant A só tocam o banco do Tenant A const tenantA = getClientForTenant('tenant-a'); await tenantA.execute('SELECT * FROM invoices'); // Tenant B tá completamente isolado const tenantB = getClientForTenant('tenant-b'); await tenantB.execute('SELECT * FROM invoices');

Benefícios do padrão:

  • Isolamento real. Sem risco de vazamento de dados entre tenants. Sem WHERE tenant_id = ? em cada query.
  • Escalamento independente. Tenants pesados têm seus próprios recursos.
  • Queries mais simples. Cada query tem scope de tenant por padrão.
  • Compliance fácil. Deleção GDPR? Apaga o arquivo do banco.
  • Backup/restore independentes. Restaurar um tenant sem afetar os outros.

Padrão 3: Cloudflare D1 com Workers

Pra aplicações já na Cloudflare, D1 dá acesso a banco com latência zero direto dos Workers:

export default { async fetch(request: Request, env: Env): Promise<Response> { // Zero salto de rede — D1 roda na mesma máquina que o Worker const { results } = await env.DB.prepare( 'SELECT * FROM products WHERE category = ?' ).bind('electronics').all(); // Batch de múltiplas operações const batchResults = await env.DB.batch([ env.DB.prepare('UPDATE inventory SET count = count - 1 WHERE id = ?').bind(productId), env.DB.prepare('INSERT INTO orders (product_id, user_id) VALUES (?, ?)').bind(productId, userId), ]); return Response.json(results); } };

Padrão 4: Local-first com sincronização

O padrão mais empolgante: apps local-first onde o banco vive no dispositivo do cliente e sincroniza com um servidor:

import { createClient } from '@libsql/client/web'; const localDb = createClient({ url: 'file:local-user.db', syncUrl: 'libsql://user-data.turso.io', authToken: userToken, }); // Funciona offline — lê e escreve no banco local await localDb.execute( 'INSERT INTO notes (title, body) VALUES (?, ?)', ['Notas da reunião', 'Discutimos o roadmap Q3...'] ); // Quando online, sincronizar com o servidor await localDb.sync();

Os números: Benchmarks do mundo real

Vamos pôr números concretos nisso. Comparação entre soluções SQLite e abordagens tradicionais.

Performance de leitura

SELECT simples por primary key (latência p50):

SQLite (arquivo local):              ~0.01ms
SQLite (réplica embarcada Turso):     ~0.02ms
Cloudflare D1 (de Worker):          ~0.5ms
PostgreSQL (mesma região):            ~1-3ms
PostgreSQL (managed, ex. Neon):       ~3-10ms
PlanetScale MySQL (regional):         ~3-8ms

Performance de escrita

INSERT simples (latência p50):

SQLite WAL (NVMe):                    ~0.05ms
SQLite (Turso, write pro primário):   ~15-50ms (rede)
Cloudflare D1 (escrita):             ~5-30ms
PostgreSQL (mesma região):            ~1-5ms

Pra escritas, soluções SQLite em rede têm latência mais alta porque escritas precisam chegar ao primário. É o trade-off: leituras ultra rápidas, escritas passam pela rede. Pra apps read-heavy (a maioria), é um trade-off excelente.

Throughput de escrita concorrente

Writes/sec sustentados (banco único):

SQLite WAL (local, NVMe):         ~10K-50K
SQLite + BEGIN CONCURRENT:        ~5K-20K (multi-writer)
Turso (primário único):           ~1K-5K (limitado por rede)
Cloudflare D1:                    ~500-2K
PostgreSQL:                       ~10K-50K
MySQL:                            ~10K-50K

Pra workloads de escrita intensa, bancos tradicionais ainda ganham. Se a app precisa de milhares de transações de escrita concorrentes por segundo — leilão em tempo real, pipeline de logging de alta frequência — PostgreSQL ou MySQL continua como melhor escolha.

Quando usar SQLite em produção (e quando não)

Use SQLite quando:

1. Sua app é read-heavy. A maioria das apps web é 90%+ leituras. Blogs, sites de conteúdo, catálogos e-commerce, dashboards — tudo read-heavy. SQLite com réplicas embarcadas é perfeito.

2. Precisa de distribuição geográfica sem complexidade. Com réplicas edge do Turso ou Cloudflare D1, seus dados ficam fisicamente mais perto dos usuários. Sem gerenciar topologias de replicação.

3. Precisa de isolamento por tenant. Apps SaaS com requisitos rígidos de isolamento se beneficiam enormemente do padrão banco-por-tenant.

4. Tá construindo apps local-first ou offline. Se a app precisa funcionar sem internet e sincronizar depois, SQLite é a base natural.

5. Quer overhead operacional mínimo. Sem servidor de banco pra gerenciar. Sem connection pooling pra tunear. SQLite é zero-ops pra muitos casos.

6. É side project ou app pequena-média. Esse é o sweet spot. A maioria esmagadora das apps web nunca escala além do que um único banco SQLite aguenta.

Não use SQLite quando:

1. Precisa de throughput alto e sustentado de escrita de múltiplas fontes. Se realmente precisa de milhares de escritas concorrentes por segundo de processos diferentes, use PostgreSQL ou MySQL.

2. Precisa de transações distribuídas complexas. SQLite não tem as capacidades de transação distribuída do PostgreSQL ou CockroachDB.

3. Precisa de Row-Level Security ou controle de acesso avançado. O RLS do PostgreSQL é battle-tested pra multi-tenant com controle de acesso granular. SQLite não tem isso.

4. O time já manja de PostgreSQL/MySQL e a infra tá montada. Não migre pra SQLite só porque tá na moda. Se o stack atual funciona bem, o custo de migração provavelmente não compensa.

O stack completo: Tudo junto

Stack pronto pra produção com SQLite em 2026:

Stack mínimo

App: Next.js / Remix / SvelteKit / Astro
DB: Turso (LibSQL) com réplica embarcada
ORM: Drizzle ORM (suporte first-class pra LibSQL/Turso)
Hosting: Vercel / Cloudflare / Fly.io

Drizzle ORM tem integração excelente com LibSQL/Turso:

// drizzle.config.ts import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/db/schema.ts', dialect: 'turso', dbCredentials: { url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN!, }, });
// src/db/schema.ts import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; export const users = sqliteTable('users', { id: integer('id').primaryKey({ autoIncrement: true }), email: text('email').notNull().unique(), name: text('name').notNull(), createdAt: text('created_at').notNull().default(sql`(datetime('now'))`), }); export const posts = sqliteTable('posts', { id: integer('id').primaryKey({ autoIncrement: true }), title: text('title').notNull(), content: text('content'), authorId: integer('author_id').references(() => users.id).notNull(), published: integer('published', { mode: 'boolean' }).default(false), });
// src/db/index.ts import { drizzle } from 'drizzle-orm/libsql'; import { createClient } from '@libsql/client'; import * as schema from './schema'; const client = createClient({ url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN!, }); export const db = drizzle(client, { schema }); const publishedPosts = await db .select() .from(schema.posts) .where(eq(schema.posts.published, true)) .leftJoin(schema.users, eq(schema.posts.authorId, schema.users.id));

Dicas de SQLite pra produção

Se vai usar SQLite em produção, essas práticas vão te salvar dos erros mais comuns:

1. Sempre ative o modo WAL

PRAGMA journal_mode=WAL;

Inegociável. WAL te dá leituras concorrentes durante escrita.

2. Configure os PRAGMAs adequados

PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000; PRAGMA synchronous = NORMAL; PRAGMA cache_size = -64000; PRAGMA foreign_keys = ON; PRAGMA temp_store = MEMORY; PRAGMA mmap_size = 268435456; -- 256MB de I/O memory-mapped PRAGMA page_size = 4096; -- Combinar com tamanho de bloco do filesystem

3. Trate SQLITE_BUSY com graciosidade

async function executeWithRetry(db, sql, args, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await db.execute({ sql, args }); } catch (err) { if (err.code === 'SQLITE_BUSY' && i < maxRetries - 1) { await new Promise(r => setTimeout(r, Math.pow(2, i) * 100)); continue; } throw err; } } }

4. Estratégia de backup

# Com Litestream — backup contínuo pro S3 litestream replicate /data/app.db s3://my-bucket/app.db # Restauração point-in-time litestream restore -o /data/restored.db s3://my-bucket/app.db # Com Turso — recuperação point-in-time embutida turso db create my-app --from-db my-app-backup --timestamp "2026-02-26T10:00:00Z"

O quadro geral: Por que isso importa

O renascimento do SQLite não é só sobre SQLite. Representa uma mudança mais ampla de como pensamos bancos de dados:

1. Localidade manda. A query mais rápida é a que não cruza limite de rede. Réplicas embarcadas e bancos edge encarnam esse princípio.

2. Simplicidade acumula. Cada config de connection pool, cada connection string do ORM, cada instância de banco gerenciado é overhead operacional. SQLite elimina a maioria. Com o tempo, essa simplicidade acumula como juros compostos.

3. A "ferramenta certa" tá sendo redefinida. Durante anos, a "ferramenta certa" pra qualquer projeto sério era PostgreSQL. Continua válido pra muitas apps. Mas pra um segmento crescente — sites de conteúdo, projetos pessoais, SaaS pequenos, apps local-first, funções edge — SQLite com ferramentas modernas é genuinamente a ferramenta certa.

4. Single-tenancy tá voltando. O padrão multi-tenant PostgreSQL (um banco, tenant_id em tudo) gera complexidade em queries, migrações, backups e compliance. Bancos SQLite por tenant oferecem uma arquitetura mais limpa pra muitas apps SaaS.

Conclusão

O renascimento do SQLite é real e não vai embora. A combinação de hardware mais rápido, inovações do LibSQL e plataformas como Turso e Cloudflare D1 resolveram limitações históricas suficientes pra fazer do SQLite uma opção de produção legítima.

Mas vamos ser claros sobre o que é e o que não é:

  • É SIM um banco de produção viável pra apps web read-heavy, SaaS com isolamento por tenant, deploys edge, apps local-first e projetos pequenos a médios.
  • NÃO É substituto pro PostgreSQL em todo cenário. Alto throughput de escrita, transações distribuídas complexas, row-level security e ecossistemas de extensões avançadas continuam sendo domínio do PostgreSQL.

O melhor conselho? Comece o próximo side project com SQLite. Não como placeholder até "o banco de verdade" — como o banco. Use Turso ou D1 pra hosting. Use Drizzle como ORM. Construa algo. Você vai se surpreender com o quão longe chega.

E se superar — que estatisticamente, a maioria dos projetos nunca faz — migrar pra PostgreSQL é um caminho bem conhecido. Mas talvez você descubra que SQLite era tudo que precisava.

O banco de dados mais implantado do mundo finalmente tá recebendo o respeito que merece do lado do servidor. E honestamente? Já era hora.

SQLiteTursoCloudflare D1LibSQLdatabaseedge computingserverlessbackend

Explore ferramentas relacionadas

Experimente estas ferramentas gratuitas do Pockit