Teil unserer Performance & Scalability-Serie
Den vollständigen Leitfaden lesenOdoo Performance Tuning: PostgreSQL und Serveroptimierung
Eine langsame Odoo-Instanz kostet Geld in Form von Produktivitätsverlusten und frustrierten Benutzern. Die gute Nachricht: Die meisten Odoo-Leistungsprobleme sind ohne Hardware-Upgrades lösbar. Die schlechte Nachricht: Um die Grundursache zu diagnostizieren, muss man den gesamten Stack verstehen – Python, PostgreSQL, Nginx, Redis und die Netzwerkschicht.
Dieser Leitfaden deckt den gesamten Lebenszyklus der Leistungsoptimierung für Odoo 19 Enterprise ab: Identifizieren von Engpässen, Optimieren von PostgreSQL, Optimieren der Odoo-Servereinstellungen, Konfigurieren des Nginx-Cachings und Anpassen der richtigen Größe Ihrer Infrastruktur an Ihre Benutzerzahl und Ihr Transaktionsvolumen.
Wichtige Erkenntnisse
- PostgreSQL-Tuning liefert die größten Leistungssteigerungen (50–300 % bei typischen Installationen)
- Gemeinsam genutzte Puffer sollten als Ausgangspunkt auf 25 % des verfügbaren RAM eingestellt werden – Odoos ORM generiert N+1 Abfragen, die mit pg_stat_statements abgefangen werden können – Indizes zu häufig gefilterten Feldern (Firmen-ID, Bundesland, Datum) sind obligatorisch – Nginx-Proxy-Caching stellt statische Assets bereit, ohne den Odoo-Server zu erreichen – Die Worker-Konfiguration wirkt sich direkt auf die Kapazität gleichzeitiger Benutzer aus – Redis-Sitzungscaching reduziert die Datenbanklast für die Authentifizierung – Vakuum- und Analysepläne müssen für Odoo-Arbeitslasten mit hohem Schreibaufwand optimiert werden
Leistungsengpässe diagnostizieren
Bevor Sie etwas tunen, stellen Sie fest, wo die tatsächliche Zeit aufgewendet wird. Blinde Optimierung verschwendet Aufwand.
Odoo-Abfrageprotokollierung aktivieren:
# 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
Aktivieren Sie die langsame PostgreSQL-Abfrageprotokollierung:
# /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
Installieren Sie pg_stat_statements (wertvollste PostgreSQL-Erweiterung):
-- 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();
Identifizieren Sie N+1 Abfragen in Odoo:
N+1-Abfragen treten auf, wenn Odoo eine Liste von Datensätzen lädt und dann eine Abfrage pro Datensatz durchführt, um zugehörige Daten abzurufen. Suchen Sie in pg_stat_statements nach Mustern wie diesem:
-- This query appearing 500 times in a single page load = N+1 problem
SELECT * FROM res_partner WHERE id = $1
Die Lösung besteht darin, den prefetch_ids-Mechanismus von Odoo zu verwenden oder select zu Ihrer ORM-Abfrage hinzuzufügen:
# 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
PostgreSQL-Konfigurationsoptimierung
PostgreSQL wird mit konservativen Standardeinstellungen ausgeliefert, die für die Ausführung auf jeder Hardware ausgelegt sind. Für einen Odoo-Produktionsserver müssen diese Standardeinstellungen angepasst werden.
Speichereinstellungen (für einen 32-GB-RAM-Server):
# /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
Verbindungseinstellungen:
# Maximum connections (Odoo workers × 2 + headroom)
max_connections = 200
# For connection pooling with PgBouncer
# If using PgBouncer, reduce to 50-100
Einstellungen des Abfrageplaners:
# 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
Autovacuum-Tuning für hohe Schreiblasten:
Die Inventar-, Buchhaltungs- und Nachrichtenmodule von Odoo erzeugen starken INSERT/UPDATE-Verkehr. Die Standardeinstellungen für das automatische Vakuum sind unzureichend:
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
Kritische Datenbankindizes
Fehlende Indizes sind nach einer schlechten Konfiguration die zweithäufigste Ursache für Odoo-Leistungsprobleme. Odoo erstellt Indizes für Primärschlüssel und einige Fremdschlüssel, aber vielen häufig gefilterten Feldern fehlen Indizes.
Überprüfen Sie fehlende Indizes mit der Ansicht 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;
Wichtige Indizes für 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);
Odoo Worker-Konfiguration
Die Anzahl der Odoo-Worker bestimmt, wie viele gleichzeitige Anfragen der Server verarbeiten kann.
Formel für die Anzahl der Arbeiter:
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)
odoo.conf-Worker-Einstellungen:
[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
Arbeitertypen verstehen:
Odoo verwendet zwei Arten von Workern:
- HTTP-Worker (
workers-Konfiguration): Verarbeitet alle Webanfragen - Cron-Worker (1 reserviert): Führen Sie geplante Aktionen im Hintergrund aus
Der Cron-Worker läuft immer, zählt aber nicht zu Ihrer HTTP-Kapazität. Stellen Sie sicher, dass auch bei Spitzenlast mindestens 1 Cron-Worker verfügbar ist.
Nginx-Konfiguration für Leistung
Nginx sitzt vor Odoo und kümmert sich um die TLS-Terminierung, die Bereitstellung statischer Dateien und optional das Caching.
Hochleistungs-Nginx-Konfiguration:
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 für Sitzung und Cache
Redis reduziert die Datenbanklast für die Sitzungsverwaltung und den ORM-Cache von Odoo erheblich.
Redis installieren und konfigurieren:
# 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
Konfigurieren Sie Odoo für die Verwendung von 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
Überwachung und fortlaufendes Leistungsmanagement
pgBadger für die PostgreSQL-Protokollanalyse einrichten:
# 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
Wichtige zu überwachende Kennzahlen:
| Metrisch | Warnschwelle | Kritischer Schwellenwert |
|---|---|---|
| Seitenladezeit | > 2 Sekunden | > 5 Sekunden |
| DB-Abfragezeit | > 100 ms Durchschnitt | > 500 ms Durchschnitt |
| Arbeitsgedächtnis | > 80 % des Grenzwerts | > 95 % des Grenzwerts |
| PostgreSQL-Verbindungen | > 70 % von max. | > 90 % von max. |
| Festplatten-IOPS | > 80 % der bereitgestellten | > 95 % der bereitgestellten |
| Cache-Trefferquote | < 95 % | < 90 % |
Häufig gestellte Fragen
Was ist die Mindestserverspezifikation für 50 gleichzeitige Odoo-Benutzer?
Für 50 gleichzeitige Benutzer mit moderatem Transaktionsvolumen: 8 vCPUs, 32 GB RAM, 500 GB SSD (NVMe bevorzugt). Die PostgreSQL-Datenbank von Odoo ist der primäre I/O-Engpass, daher ist schneller Speicher wichtiger als reine CPU-Geschwindigkeit. Konfigurieren Sie 13 Worker (8×2-3 für Headroom), 8 GB Shared_Buffers und stellen Sie sicher, dass sich Ihre Datenbank auf dem SSD-Volume befindet.
Wie kann ich feststellen, ob meine Odoo-Langsamkeit Python oder PostgreSQL ist?
Verwenden Sie den integrierten Profiler von Odoo (Einstellungen → Technik → Leistung → Profiler im Entwicklermodus), um einen langsamen Vorgang aufzuzeichnen. Der Flamegraph zeigt an, ob Zeit mit Python-Code oder dem Warten auf SQL-Ergebnisse verbracht wird. Wenn SQL-Abfragen dominieren, konzentrieren Sie sich auf PostgreSQL-Tuning und -Indizes. Wenn Python dominiert, suchen Sie nach fehlendem @api.depends-Caching oder Ineffizienzen bei benutzerdefiniertem Code.
Soll ich PgBouncer für das Verbindungspooling verwenden?
Ja, für Bereitstellungen mit mehr als 30 Odoo-Workern oder starkem API-Verkehr. Mit PgBouncer im Transaktionsmodus-Pooling können viele Odoo-Worker einen kleineren Pool tatsächlicher PostgreSQL-Verbindungen gemeinsam nutzen, wodurch der Overhead pro Verbindung reduziert wird. Konfigurieren Sie max_connections in PostgreSQL auf 50–100, wenn Sie PgBouncer verwenden, und stellen Sie dann die pool_size von PgBouncer so ein, dass sie mit Ihrer Odoo-Worker-Anzahl übereinstimmt.
Wie oft sollte ich VACUUM ANALYZE in einer Odoo-Datenbank ausführen?
Autovacuum erledigt dies automatisch, wenn es richtig konfiguriert ist. Nach der oben genannten Optimierung (aggressive Skalierungsfaktoren, mehr Worker) sollte Autovacuum kontinuierlich auf aktiven Tischen laufen. Führen Sie SELECT schemaname, tablename, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20; aus, um zu überprüfen, ob Tabellen regelmäßig gelöscht werden.
Welche Auswirkungen haben zu viele Odoo-Mitarbeiter?
Jeder Odoo-Worker verbraucht mindestens 256–512 MB RAM. Zu viele Worker führen zu einer Speichererschöpfung, was zum Absturz der Worker führt (limit_memory_hard), was zu HTTP 500-Fehlern für Benutzer führt. Darüber hinaus können zu viele PostgreSQL-Verbindungen (Worker × max_db_connections) die Datenbank überlasten. Beginnen Sie mit der Formel (CPU×2+1), überwachen Sie den Speicher unter Last und passen Sie ihn bei Bedarf nach unten an.
Nächste Schritte
Die Leistungsoptimierung von Odoo ist ein iterativer Prozess. Eine einzige Tuning-Sitzung bringt erhebliche Verbesserungen, aber eine nachhaltige Leistung erfordert eine fortlaufende Überwachung, regelmäßige Indexanalysen und Konfigurationsanpassungen, wenn Ihr Datenvolumen wächst.
ECOSIRE bietet Odoo-Leistungsprüfungen für Unternehmensbereitstellungen an und ermittelt die wirkungsvollsten Optimierungen für Ihre spezifische Arbeitslast, Transaktionsmuster und Infrastruktur. Unsere Ingenieure haben Odoo-Installationen von 10-Benutzer-KMUs bis hin zu 500-Benutzer-Unternehmensbereitstellungen optimiert.
Fordern Sie ein Odoo-Leistungsaudit von ECOSIRE an →
Teilen Sie uns Ihre aktuellen Serverspezifikationen, die Benutzerzahl und die bei Ihnen auftretenden Symptome mit. Unser Team ermittelt dann die Grundursachen und erstellt einen priorisierten Optimierungsplan.
Geschrieben von
ECOSIRE Research and Development Team
Entwicklung von Enterprise-Digitalprodukten bei ECOSIRE. Einblicke in Odoo-Integrationen, E-Commerce-Automatisierung und KI-gestützte Geschäftslösungen.
Verwandte Artikel
Odoo Accounting vs QuickBooks: Detailed Comparison 2026
In-depth 2026 comparison of Odoo Accounting vs QuickBooks covering features, pricing, integrations, scalability, and which platform fits your business needs.
Case Study: eCommerce Migration to Shopify with Odoo Backend
How a fashion retailer migrated from WooCommerce to Shopify and connected it to Odoo ERP, cutting order fulfillment time by 71% and growing revenue 43%.
Case Study: Manufacturing ERP Implementation with Odoo 19
How a Pakistani auto-parts manufacturer cut order processing time by 68% and reduced inventory variance to under 2% with ECOSIRE's Odoo 19 implementation.
Mehr aus Performance & Scalability
k6 Load Testing: Stress-Test Your APIs Before Launch
Master k6 load testing for Node.js APIs. Covers virtual user ramp-ups, thresholds, scenarios, HTTP/2, WebSocket testing, Grafana dashboards, and CI integration patterns.
Nginx Production Configuration: SSL, Caching, and Security
Nginx production configuration guide: SSL termination, HTTP/2, caching headers, security headers, rate limiting, reverse proxy setup, and Cloudflare integration patterns.
Odoo vs Acumatica: Cloud ERP for Growing Businesses
Odoo vs Acumatica compared for 2026: unique pricing models, scalability, manufacturing depth, and which cloud ERP fits your growth trajectory.
Testing and Monitoring AI Agents in Production
A complete guide to testing and monitoring AI agents in production environments. Covers evaluation frameworks, observability, drift detection, and incident response for OpenClaw deployments.
Compliance Monitoring Agents with OpenClaw
Deploy OpenClaw AI agents for continuous compliance monitoring. Automate regulatory checks, policy enforcement, audit trail generation, and compliance reporting.
Optimizing AI Agent Costs: Token Usage and Caching
Practical strategies for reducing AI agent operational costs through token optimization, caching, model routing, and usage monitoring. Real savings from production OpenClaw deployments.