Otimizando PostgreSQL para Aplicações .NET

Ao desenvolver aplicações de alta performance com .NET e PostgreSQL, a otimização do banco de dados pode ser a diferença entre um sistema fluído e um gargalo constante. Neste artigo, veremos boas práticas, ajustes no PostgreSQL e otimizações no lado .NET.


Por que PostgreSQL no .NET?

O PostgreSQL é um dos bancos de dados relacionais mais robustos e flexíveis. Com o driver Npgsql, integra-se nativamente ao .NET, oferecendo suporte a recursos avançados como JSONB, busca textual e particionamento.


Gargalos Comuns de Performance

  • Índices mal otimizados.
  • Excesso de conexões abertas/fechadas.
  • Escritas em lotes grandes sem batch.
  • Estatísticas desatualizadas levando a planos ruins.

Veja também: Hangfire e Filas Descentralizadas


Configuração do PostgreSQL para Alta Performance

ParâmetroAjuste RecomendadoImpacto
max_connectionsAjustar conforme limite do poolEvita sobrecarga
shared_buffers25-40% da RAMCache rápido
work_mem4–64MB por queryMelhor sorting
maintenance_work_mem256MB+Índices mais rápidos
effective_cache_size50-75% da RAMMelhor planejamento

Exemplo: postgresql.conf

max_connections = 200
shared_buffers = 4GB
work_mem = 16MB
maintenance_work_mem = 512MB
effective_cache_size = 12GB

Boas Práticas de Modelagem no .NET

  • Escolher tipos corretos (uuid, timestamp with time zone).
  • Evitar SELECT * em produção.
  • Usar JSONB para partes flexíveis do schema.
  • Particionar dados grandes por tempo ou chave.

Pool de Conexões no .NET

using Npgsql;
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var connString = "Host=localhost;Username=appuser;Password=secret;Database=mydb;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=50;Timeout=15";

        await using var conn = new NpgsqlConnection(connString);
        await conn.OpenAsync();

        await using var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM users", conn);
        var count = await cmd.ExecuteScalarAsync();

        Console.WriteLine($"Total de usuários: {count}");
    }
}

Dicas:

  • Ativar Pooling=true.
  • Ajustar Minimum e Maximum Pool Size.
  • Monitorar pg_stat_activity.

Monitoramento e Métricas

  • Habilitar pg_stat_statements para queries lentas.

  • Checar cache hit ratio:

    SELECT sum(blks_hit) / sum(blks_hit + blks_read) AS cache_hit_ratio FROM pg_stat_database;
    
  • Integrar com Grafana + Prometheus.


Exemplo Completo: Bulk Insert Otimizado

using Npgsql;
using System;
using System.Threading.Tasks;

class BulkInsertExample
{
    public static async Task Main()
    {
        var connString = "Host=localhost;Username=appuser;Password=secret;Database=mydb;Pooling=true";

        await using var conn = new NpgsqlConnection(connString);
        await conn.OpenAsync();

        using var writer = conn.BeginBinaryImport("COPY products (id, name, price) FROM STDIN (FORMAT BINARY)");

        for (int i = 0; i < 1000; i++)
        {
            writer.StartRow();
            writer.Write(Guid.NewGuid(), NpgsqlTypes.NpgsqlDbType.Uuid);
            writer.Write($"Produto {i}", NpgsqlTypes.NpgsqlDbType.Text);
            writer.Write(i * 10.5m, NpgsqlTypes.NpgsqlDbType.Numeric);
        }

        await writer.CompleteAsync();
        Console.WriteLine("Inserção em lote concluída.");
    }
}

Referências Externas


Conecte-se comigo no LinkedIn e compartilhe suas dicas de otimização de PostgreSQL para .NET.