- A estrutura de gravador único e a natureza embarcada do SQLite foram comprovadas experimentalmente como fatores que, na prática, aumentam escalabilidade e desempenho
- Nas mesmas condições, o Postgres caiu para 348 TPS com latência de rede, enquanto o SQLite, ao eliminar a rede, alcançou 44.096 TPS
- Com processamento em lote e transações granularizadas com base em SAVEPOINT, aproveitando o modelo de gravador único, registrou até 186.157 TPS e, em uma configuração estável, 102.545 TPS
- A Lei de Amdahl explica os gargalos dos bancos de dados baseados em rede, e o SQLite mantém alta eficiência justamente por evitá-los
- O resultado destaca o potencial do SQLite em ambientes locais e a importância de eliminar gargalos de rede
Estrutura do SQLite e ambiente experimental
- O SQLite não tem MVCC e permite apenas um gravador, mas essa estrutura acaba possibilitando alta escalabilidade
- Como banco de dados embarcado, não há sobrecarga de rede
- O benchmark foi executado em um MacBook Pro (2021) com Apple M1 Pro e 16 GB de memória
- O experimento não buscou uma otimização perfeita, mas sim mostrar que é possível alcançar alta taxa de escrita mesmo em condições comuns
Definição de TPS e exemplo de transação
- TPS não significa apenas velocidade bruta de escrita, mas sim transações interativas (Interactive Transaction)
- Ex.: em uma transferência entre contas, várias consultas e código da aplicação são executados dentro de uma única transação
- Como a transação pode reverter o estado em caso de erro, ela tem papel central na manutenção da consistência
Configuração do benchmark
- Foram usadas virtual threads baseadas em Clojure para simular um grande volume de requisições simultâneas
- O Postgres foi configurado com pool de conexões baseado em HikariCP, enquanto o SQLite usou um único gravador e conexões de leitura equivalentes ao número de núcleos
- Ambos os bancos usaram uma tabela
account simples com os campos id, balance, inserindo 1 bilhão de linhas
- A atividade dos usuários segue uma distribuição de lei de potência (0.9995), com cerca de 100 mil usuários ativos
Desempenho do banco de dados em rede (Postgres)
- No mesmo servidor, o Postgres atingiu 13.756 TPS
- Ao adicionar 5 ms de latência de rede, caiu para 1.214 TPS; com 10 ms, para 702 TPS
- Após aplicar nível de isolamento serializável, caiu para 660 TPS e, com consultas adicionais, para 348 TPS
- Isso mostra, de acordo com a Lei de Amdahl, que o gargalo de rede limita o desempenho total
- À medida que a latência de rede aumenta, a contenção por locks de transação se intensifica e a escalabilidade desaparece
Vantagens do SQLite como embarcado
- Ao remover a rede, o SQLite alcançou 44.096 TPS
- Com o gargalo de rede eliminado, o impacto da Lei de Amdahl é minimizado
- Aplicando processamento em lote (batch processing) e explorando a estrutura de gravador único, o desempenho subiu para 186.157 TPS
- O ajuste dinâmico do tamanho do lote otimiza automaticamente latência (latency) e vazão (throughput)
Transações granularizadas com SAVEPOINT
- Para evitar que falhas em transações individuais afetem o lote inteiro, foram aplicadas transações aninhadas com SAVEPOINT
- Em caso de falha, apenas aquela transação é revertida, e o lote inteiro é preservado
- Mesmo assim, o sistema manteve 121.922 TPS
Teste de carga mista de leitura/escrita
- A carga foi composta por 75% de leituras e 25% de escritas
- Foi usado um pool separado de threads de leitura para isolar as leituras, impedindo que afetassem as escritas
- Como resultado, foram alcançados 102.545 TPS
Resumo da comparação de desempenho
| Condição |
Postgres |
SQLite |
| Sem rede |
13.756 |
44.096 |
| Latência de 5 ms |
1.214 |
n/a |
| Latência de 10 ms |
702 |
n/a |
| 10 ms + serialização |
660 |
n/a |
| Processamento em lote |
n/a |
186.157 |
| Lote + SAVEPOINT |
n/a |
121.922 |
| Lote + SAVEPOINT + leitura |
n/a |
102.545 |
Conclusão
- O SQLite alcança TPS muito mais altos que bancos de dados baseados em rede graças ao modelo de gravador único e à estrutura embarcada
- Ao evitar os limites de gargalo de rede apontados pela Lei de Amdahl, ele maximiza a eficiência
- Todo o código foi publicado no GitHub, e também foram apresentados materiais relacionados sobre Lei de Amdahl, lei de potência e casos de escalabilidade com SQLite
- O SQLite é uma opção altamente eficaz para processamento transacional de alto desempenho em ambientes locais
2 comentários
Então, se for usar só em ambiente local sem recorrer a servidores externos, a ideia é que não há necessidade de pagar o imposto da rede. (VFS vs Socket)
Opinião no Hacker News
Estou criando um servidor ORM/CRUD híbrido com protobuf baseado em SQLite
O código e a explicação estão em GitHub - accretional/collector
Com backup em tempo real, é possível ter 5~15ms de indisponibilidade, enfileirar centenas de solicitações de leitura/escrita, latência total de CRUD na casa de 1ms e até backup por streaming com base em WAL
Antes eu só usava Postgres e Spanner, mas se o Collector ganhar apenas um recurso de particionamento, acho que não volto a usar Postgres
A desvantagem é que todos os dados e operações precisam caber em uma única máquina
Com uma instância AWS u-24tb1.112xlarge (448 vcores, 24TB de RAM, 64TB de EBS), há bastante folga
O texto enfatiza a eficiência do SQLite, mas sinto que o critério de comparação não está claro
Isso porque ele parte de uma arquitetura originalmente separada em servidores e depois mede o desempenho de um banco embarcado local
Nas mesmas condições, um Postgres local bem ajustado também pode entregar desempenho parecido
Limitar o número de conexões do Postgres a 8 pode ser um gargalo
Seria bom divulgar também o uso de CPU e threads e refazer o teste com um pool de conexões maior
Se aumentar para 64 conexões, o throughput pode subir 8 vezes. É preciso ampliar a configuração do cliente até atingir o limite
O ponto principal é reconhecer se a latência de rede é o gargalo
Em muitas cargas de trabalho, um banco local comum é mais rápido que um ótimo banco remoto
O importante não é “qual banco é o melhor”, e sim “há necessidade de atravessar a fronteira da rede?”
Bancos de dados em rede têm a vantagem de facilitar o redeploy da aplicação
Dá para subir uma nova instância e encerrar a antiga, possibilitando um deploy quase sem downtime
Se o SQLite estiver na mesma instância, é preciso subir o banco de novo na hora da troca, o que complica mais. Fico curioso se você já enfrentou esse problema em produção
Durante migrações, pode haver downtime. Graças ao Litestream, replicação e backup ficaram mais fáceis agora
O autor configurou
PRAGMA synchronous="normal", o que significa que não faz fsync a cada vezPara uma comparação justa, deveria usar
"full""normal"também é aceitável. Em caso de queda de energia, perde-se durabilidade, mas a consistência da transação é mantidaFico curioso sobre como seria uma configuração de HA (alta disponibilidade) para SQLite
No mínimo, deveria ser algo com failover automático
No momento, estou em dúvida entre Postgres e SQLite (incluindo litestream).
Meu app tolera um pouco de downtime, então escalar verticalmente em uma única máquina é mais simples e barato
O Marmot no GitHub recebeu um novo mecanismo de replicação baseado em gossip
Fico curioso se existem casos reais em que o SQLite foi levado ao limite em produção
Em webapps comuns ou ambientes de e-commerce, fico curioso sobre qual seria o limite de usuários entre SQLite e Postgres
Com atualizações recentes, o SQLite permite leituras simultâneas, mas ainda só aceita uma única escrita
Queria saber em que casos isso vira problema e se, pensando em escalar, não seria melhor começar logo com Postgres