Pular para o conteúdo

Em produção

Pool, índices, o que medir, o que nunca fazer — e as decisões que só aparecem quando há carga.

O que funciona numa máquina com um usuário falha com cinquenta. Esta página é a lista do que muda.

Uma conexão por pedido não escala#

Abrir conexão com Postgres custa um aperto de mão TCP, autenticação e alocação de um processo do lado do servidor — dezenas de milissegundos. Por pedido, isso domina o tempo de resposta; e o servidor tem um teto de conexões que se atinge antes do teto de CPU.

dataforge
adopt Arcane.Forge as Forge

// O pool abre N conexoes e as empresta. 'conexao' devolve ao fim.
pool := Forge.pool(":memory:", 4)

// Sempre com 'with': uma conexao pegada e nao devolvida some do
// pool para sempre, e o 'with' devolve inclusive se o corpo falhar.
cycle i from 1 to 8:
    with Forge.conexao(pool) as db:
        Forge.consultar(db, "select 1 as um")

out $"oito pedidos, {pool.estado()['tamanho']} conexoes"

O índice que falta#

A consulta que responde em 3 ms com mil linhas responde em 3 s com um milhão — e nada no código mudou. explain mostra o plano, e é ele que diz se o banco está varrendo a tabela.

dataforge
adopt Arcane.Database as Db

db := Db.connect(":memory:")
Db.execute(db, "create table pedido (id integer primary key, cliente_id integer, total real)")

cycle i from 1 to 500:
    Db.execute(db, "insert into pedido (cliente_id, total) values (?, ?)",
        [i % 50, i * 1.5])

// Sem indice: varredura.
antes := Db.explain(db, "select * from pedido where cliente_id = 7")
out $"antes:  {antes}"

Db.execute(db, "create index idx_pedido_cliente on pedido (cliente_id)")

depois := Db.explain(db, "select * from pedido where cliente_id = 7")
out $"depois: {depois}"

Db.close(db)
IndexePorque
toda chave estrangeiraé por onde as relações do ORM buscam — e sem índice cada com() vira varredura
a coluna de todo onde frequenteé o caso óbvio, e o mais esquecido
a coluna de ordenar quando há limitesem ele o banco ordena a tabela inteira para devolver dez linhas
não indexe tudotodo índice é escrito em cada insert; uma tabela com oito índices escreve nove vezes

O que nunca fazer#

NuncaO que acontece
select * numa listagemtraz colunas grandes que a tela não usa, e o custo é de rede
listagem sem limitea forma mais comum de derrubar uma aplicação — e a paginar do ORM já vem com teto
consulta dentro de laçoo N+1: use `com()`
order by por coluna vinda da URL, sem listainjeção por identificador; o order_by do Arcane.Database recusa o que não parece nome
migração sem downdesfazer exige editar o banco à mão
transação aberta esperando redeela segura locks enquanto o HTTP de terceiro demora

O que medir#

MedidaComoO que ela denuncia
consultas por pedidocontar no logN+1 — o número cresce com o tamanho da página
p95 da consultaArcane.Perfila média esconde a cauda, e é a cauda que o usuário sente
conexões em usopool.estado()pool no teto = pedidos esperando
plano da consulta lentaDb.explainíndice faltando
tamanho das tabelasDb.statsa tabela que cresce sem retenção

Continue em Transações e pool e O banco em contêiner.