Odoo Performance Tuning: PostgreSQL and Server Optimization

Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.

E
ECOSIRE Research and Development Team
|19 de março de 202610 min de leitura2.3k Palavras|

Parte da nossa série Performance & Scalability

Leia o guia completo

Odoo Performance Tuning: PostgreSQL e otimização de servidor

Uma instância lenta do Odoo custa dinheiro em perda de produtividade e usuários frustrados. A boa notícia: a maioria dos problemas de desempenho do Odoo podem ser resolvidos sem atualizações de hardware. A má notícia: diagnosticar a causa raiz requer a compreensão de toda a pilha – Python, PostgreSQL, Nginx, Redis e a camada de rede.

Este guia cobre todo o ciclo de vida de otimização de desempenho do Odoo 19 Enterprise: identificação de gargalos, ajuste do PostgreSQL, otimização das configurações do servidor Odoo, configuração do cache Nginx e dimensionamento correto de sua infraestrutura para contagem de usuários e volume de transações.

Principais conclusões

  • O ajuste do PostgreSQL oferece os maiores ganhos de desempenho (50-300% em instalações típicas)
  • Os buffers compartilhados devem ser definidos para 25% da RAM disponível como ponto de partida
  • O ORM do Odoo gera consultas N+1 que podem ser capturadas com pg_stat_statements
  • Os índices em campos filtrados com frequência (company_id, state, date) são obrigatórios
  • O cache do proxy Nginx atende ativos estáticos sem atingir o servidor Odoo
  • A configuração do trabalhador impacta diretamente a capacidade do usuário simultâneo
  • O cache da sessão Redis reduz a carga do banco de dados para autenticação
  • As programações de vácuo e análise devem ser ajustadas para cargas de trabalho Odoo de alta gravação

Diagnosticando gargalos de desempenho

Antes de ajustar qualquer coisa, identifique onde o tempo está realmente sendo gasto. A otimização cega desperdiça esforço.

Ative o registro de consultas do Odoo:

# odoo.conf
[options]
log_level = info
logfile = /var/log/odoo/odoo.log

# For SQL query logging (development/staging only):
log_handler = odoo.sql_db:DEBUG

Ative o registro de consultas lentas do PostgreSQL:

# /etc/postgresql/15/main/postgresql.conf
log_min_duration_statement = 1000  # Log queries taking > 1 second
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_checkpoints = on
log_connections = on
log_lock_waits = on

Instale pg_stat_statements (extensão PostgreSQL mais valiosa):

-- Enable the extension
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Find the top 20 slowest queries
SELECT
    round(total_exec_time::numeric, 2) AS total_ms,
    calls,
    round(mean_exec_time::numeric, 2) AS mean_ms,
    round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 2) AS pct,
    left(query, 100) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

-- Reset statistics after tuning to measure improvement
SELECT pg_stat_statements_reset();

Identifique consultas N+1 no Odoo:

Consultas N+1 ocorrem quando o Odoo carrega uma lista de registros e então faz uma consulta por registro para buscar dados relacionados. Procure padrões como este em pg_stat_statements:

-- This query appearing 500 times in a single page load = N+1 problem
SELECT * FROM res_partner WHERE id = $1

A correção é usar o mecanismo prefetch_ids do Odoo ou adicionar select à sua consulta ORM:

# Bad: triggers N+1 for partner on each order
for order in orders:
    print(order.partner_id.name)  # One query per order

# Good: prefetch partner data in one query
orders = self.env['sale.order'].search([...])
orders.mapped('partner_id')  # Forces prefetch
for order in orders:
    print(order.partner_id.name)  # No additional queries

Ajuste de configuração do PostgreSQL

O PostgreSQL vem com padrões conservadores projetados para rodar em qualquer hardware. Para um servidor de produção Odoo, esses padrões devem ser ajustados.

Configurações de memória (para um servidor de 32 GB de RAM):

# /etc/postgresql/15/main/postgresql.conf

# Shared buffers: 25% of RAM
shared_buffers = 8GB

# Work memory: per-operation memory for sorts/joins
# Start conservative, increase if you see disk sorts
work_mem = 64MB

# Maintenance work memory: for VACUUM, CREATE INDEX
maintenance_work_mem = 2GB

# Effective cache size: tells planner how much OS cache is available
# Set to 75% of total RAM
effective_cache_size = 24GB

# WAL settings for better write performance
wal_buffers = 256MB
checkpoint_completion_target = 0.9
checkpoint_timeout = 15min
max_wal_size = 4GB
min_wal_size = 1GB

Configurações de conexão:

# Maximum connections (Odoo workers × 2 + headroom)
max_connections = 200

# For connection pooling with PgBouncer
# If using PgBouncer, reduce to 50-100

Configurações do planejador de consultas:

# Enable parallel query execution
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
max_worker_processes = 16

# SSD storage: random_page_cost should equal seq_page_cost
random_page_cost = 1.1  # Default is 4.0 (for spinning disk)
seq_page_cost = 1.0

# Increase statistics target for better query plans on Odoo's large tables
default_statistics_target = 200

Ajuste de vácuo automático para cargas de trabalho de alta gravação:

Os módulos de inventário, contabilidade e mensagens do Odoo geram tráfego pesado de INSERT/UPDATE. As configurações padrão de vácuo automático são insuficientes:

autovacuum = on
autovacuum_max_workers = 5
autovacuum_naptime = 30s
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
autovacuum_vacuum_scale_factor = 0.01   # Vacuum when 1% of rows are dead
autovacuum_analyze_scale_factor = 0.005  # Analyze when 0.5% of rows change
autovacuum_vacuum_cost_delay = 2ms       # Reduce I/O throttling

Índices críticos de banco de dados

Índices ausentes são a segunda causa mais comum de problemas de desempenho do Odoo, depois de uma configuração inadequada. Odoo cria índices para chaves primárias e algumas chaves estrangeiras, mas muitos campos comumente filtrados não possuem índices.

Verifique os índices ausentes usando a visualização pg_missing_fk_indexes:

-- Find foreign keys without indexes
SELECT
    tc.table_name,
    kcu.column_name,
    ccu.table_name AS foreign_table_name,
    pg_relation_size(tc.table_name::regclass) AS table_size
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
    ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu
    ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND NOT EXISTS (
    SELECT 1 FROM pg_indexes pi
    WHERE pi.tablename = tc.table_name
    AND pi.indexdef LIKE '%' || kcu.column_name || '%'
)
ORDER BY table_size DESC;

Índices essenciais para Odoo 19:

-- Sale orders (most queried table)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sale_order_state
    ON sale_order(state);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sale_order_company_date
    ON sale_order(company_id, date_order DESC);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_sale_order_partner
    ON sale_order(partner_id) WHERE state != 'cancel';

-- Account moves (invoicing)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_move_state_type
    ON account_move(state, move_type);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_move_company_date
    ON account_move(company_id, invoice_date DESC);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_move_partner
    ON account_move(partner_id) WHERE state = 'posted';

-- Account move lines (most queried for reconciliation)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_move_line_account_reconcile
    ON account_move_line(account_id, reconciled, date);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_account_move_line_move_date
    ON account_move_line(move_id, date);

-- Stock moves (inventory)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_stock_move_state_product
    ON stock_move(state, product_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_stock_quant_product_location
    ON stock_quant(product_id, location_id);

-- Mail messages (can grow very large)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mail_message_res_model_id
    ON mail_message(res_model, res_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mail_message_date
    ON mail_message(date DESC);

-- IR rule performance (access control)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_ir_rule_model_groups
    ON ir_rule(model_id);

Configuração do trabalhador Odoo

O número de trabalhadores Odoo determina quantas solicitações simultâneas o servidor pode manipular.

Fórmula para contagem de trabalhadores:

Workers = (CPU_cores × 2) + 1
Memory per worker: 256MB - 512MB depending on workload

Example for 8 CPU cores, 32GB RAM:
Workers = (8 × 2) + 1 = 17
Memory check: 17 × 512MB = 8.5GB (well within 32GB)

Configurações do trabalhador odoo.conf:

[options]
# Worker processes
workers = 17

# Limits to prevent runaway workers
limit_memory_hard = 2684354560  # 2.5GB hard limit (kills worker)
limit_memory_soft = 2147483648  # 2GB soft limit (triggers gc)
limit_time_cpu = 600            # CPU seconds per request
limit_time_real = 1200          # Wall clock seconds per request
limit_request = 8192            # Requests before worker restart

# Long polling (for live notifications)
longpolling_port = 8072

Compreendendo os tipos de trabalhadores:

Odoo usa dois tipos de trabalhadores:

  1. Trabalhadores HTTP (configuração workers): Lidar com todas as solicitações da web
  2. Cron workers (1 reservado): executa ações agendadas em segundo plano

O cron trabalhador está sempre em execução, mas não conta para sua capacidade HTTP. Certifique-se de que pelo menos 1 cron trabalhador esteja disponível mesmo no pico de carga.


Configuração Nginx para desempenho

O Nginx fica na frente do Odoo e lida com a terminação TLS, serviço de arquivos estáticos e, opcionalmente, armazenamento em cache.

Configuração Nginx de alto desempenho:

upstream odoo {
    server 127.0.0.1:8069 weight=1 fail_timeout=0;
}

upstream odoochat {
    server 127.0.0.1:8072 weight=1 fail_timeout=0;
}

# Cache zone for static assets
proxy_cache_path /var/cache/nginx/odoo
    levels=1:2
    keys_zone=odoo_cache:100m
    max_size=1g
    inactive=60m
    use_temp_path=off;

server {
    listen 443 ssl http2;
    server_name your-odoo.com;

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript
               text/xml application/xml application/xml+rss text/javascript;
    gzip_min_length 1000;
    gzip_comp_level 6;

    # Static file caching
    location /web/static/ {
        proxy_cache odoo_cache;
        proxy_cache_valid 200 7d;
        proxy_cache_use_stale error timeout updating
            http_500 http_502 http_503 http_504;
        add_header X-Cache-Status $upstream_cache_status;
        expires 7d;
        proxy_pass http://odoo;
    }

    # Long polling for live chat/notifications
    location /web/longpolling {
        proxy_pass http://odoochat;
        proxy_read_timeout 3600s;
        proxy_connect_timeout 300s;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Main application
    location / {
        proxy_pass http://odoo;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 720s;
        proxy_connect_timeout 300s;
        proxy_buffering on;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;

        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header Referrer-Policy strict-origin-when-cross-origin;
    }
}

Redis para sessão e cache

O Redis reduz significativamente a carga do banco de dados para gerenciamento de sessões e cache ORM do Odoo.

Instalar e configurar o Redis:

# Install Redis
sudo apt install redis-server

# Configure Redis for Odoo (max 4GB memory, LRU eviction)
sudo nano /etc/redis/redis.conf
# redis.conf
maxmemory 4gb
maxmemory-policy allkeys-lru
save ""  # Disable persistence for pure cache
tcp-keepalive 300

Configure o Odoo para usar Redis:

# odoo.conf
[options]
# Redis for session storage
session_redis_host = 127.0.0.1
session_redis_port = 6379
session_redis_prefix = odoo_session_

# Redis for IR rules and ORM cache
cache_redis_host = 127.0.0.1
cache_redis_port = 6379

Monitoramento e gerenciamento contínuo de desempenho

Configure o pgBadger para análise de log do PostgreSQL:

# Install pgBadger
sudo apt install pgbadger

# Generate report from PostgreSQL logs
pgbadger /var/log/postgresql/postgresql-15-main.log \
    -o /var/www/html/pgbadger/index.html \
    --format html \
    --top 20

Principais métricas a serem monitoradas:

MétricaLimite de avisoLimiar Crítico
Tempo de carregamento da página> 2 segundos> 5 segundos
Tempo de consulta ao banco de dados> Média de 100ms> Média de 500ms
Memória do trabalhador> 80% do limite> 95% do limite
Conexões PostgreSQL> 70% do máximo> 90% do máximo
IOPS de disco> 80% do provisionado> 95% do provisionado
Proporção de acertos de cache<95%<90%

Perguntas frequentes

Qual é a especificação mínima de servidor para 50 usuários simultâneos do Odoo?

Para 50 usuários simultâneos com volume de transações moderado: 8 vCPUs, 32 GB de RAM, SSD de 500 GB (preferencialmente NVMe). O banco de dados PostgreSQL do Odoo é o principal gargalo de E/S, portanto, o armazenamento rápido é mais importante do que a velocidade bruta da CPU. Configure 13 trabalhadores (8×2-3 para espaço livre), 8 GB shared_buffers e certifique-se de que seu banco de dados esteja no volume SSD.

Como faço para diagnosticar se minha lentidão no Odoo é Python ou PostgreSQL?

Use o criador de perfil integrado do Odoo (Configurações → Técnico → Desempenho → Criador de perfil no modo de desenvolvedor) para registrar uma operação lenta. O flamegraph mostrará se o tempo é gasto no código Python ou aguardando resultados SQL. Se as consultas SQL dominarem, concentre-se no ajuste e nos índices do PostgreSQL. Se o Python dominar, procure por falta de cache @api.depends ou ineficiências de código personalizado.

Devo usar o PgBouncer para pool de conexões?

Sim, para implantações com mais de 30 trabalhadores Odoo ou tráfego intenso de API. O PgBouncer no pool de modo de transação permite que muitos trabalhadores Odoo compartilhem um pool menor de conexões reais do PostgreSQL, reduzindo a sobrecarga por conexão. Configure max_connections no PostgreSQL para 50-100 ao usar o PgBouncer e, em seguida, defina o pool_size do PgBouncer para corresponder à contagem de trabalhadores do Odoo.

Com que frequência devo executar VACUUM ANALYZE em um banco de dados Odoo?

O Autovacuum lida com isso automaticamente se configurado corretamente. Após o ajuste acima (fatores de escala agressivos, mais trabalhadores), o autovacuum deverá funcionar continuamente nas tabelas ativas. Execute SELECT schemaname, tablename, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20; para verificar se as tabelas estão sendo limpas com frequência.

Qual é o impacto de muitos trabalhadores Odoo?

Cada trabalhador Odoo consome no mínimo 256–512 MB de RAM. Muitos trabalhadores levam ao esgotamento da memória, fazendo com que os trabalhadores travem (limit_memory_hard), o que resulta em erros HTTP 500 para os usuários. Além disso, muitas conexões PostgreSQL (workers × max_db_connections) podem sobrecarregar o banco de dados. Comece com a fórmula (CPU×2+1), monitore a memória sob carga e ajuste para baixo, se necessário.


Próximas etapas

O ajuste de desempenho do Odoo é um processo iterativo. Uma única sessão de ajuste proporciona ganhos significativos, mas o desempenho sustentado requer monitoramento contínuo, análise periódica de índice e ajustes de configuração à medida que o volume de dados aumenta.

ECOSIRE fornece auditorias de desempenho Odoo para implantações empresariais, identificando as otimizações de maior impacto para sua carga de trabalho, padrões de transação e infraestrutura específicos. Nossos engenheiros ajustaram instalações Odoo de pequenas e médias empresas com 10 usuários a implantações empresariais com 500 usuários.

Solicite uma auditoria de desempenho Odoo da ECOSIRE →

Compartilhe as especificações atuais do servidor, a contagem de usuários e os sintomas que você está enfrentando, e nossa equipe identificará as causas principais e fornecerá um plano de otimização priorizado.

E

Escrito por

ECOSIRE Research and Development Team

Construindo produtos digitais de nível empresarial na ECOSIRE. Compartilhando insights sobre integrações Odoo, automação de e-commerce e soluções de negócios com IA.

Converse no WhatsApp