属于我们的Performance & Scalability系列
阅读完整指南Odoo 性能调优:PostgreSQL 和服务器优化
缓慢的 Odoo 实例会导致生产力下降和用户沮丧,从而造成经济损失。好消息:大多数 Odoo 性能问题无需升级硬件即可解决。坏消息:诊断根本原因需要了解整个堆栈——Python、PostgreSQL、Nginx、Redis 和网络层。
本指南涵盖了 Odoo 19 Enterprise 的完整性能优化生命周期:识别瓶颈、调整 PostgreSQL、优化 Odoo 服务器设置、配置 Nginx 缓存以及根据用户数量和交易量调整基础设施规模。
要点
- PostgreSQL 调优可带来最大的性能提升(典型安装中为 50-300%)
- 共享缓冲区应设置为可用 RAM 的 25% 作为起点
- Odoo 的 ORM 生成可以用 pg_stat_statements 捕获的 N+1 查询
- 频繁过滤字段(company_id、state、date)的索引是强制性的
- Nginx 代理缓存无需访问 Odoo 服务器即可提供静态资源
- Worker配置直接影响并发用户容量
- Redis会话缓存减少了身份验证的数据库负载
- 必须针对高写入 Odoo 工作负载调整清理和分析计划
诊断性能瓶颈
在调整任何内容之前,先确定时间实际花在哪里。盲目优化浪费精力。
启用 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
启用 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
安装pg_stat_statements(最有价值的PostgreSQL扩展):
-- 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();
在 Odoo 中识别 N+1 查询:
当 Odoo 加载记录列表,然后对每个记录进行一次查询以获取相关数据时,会发生 N+1 查询。在 pg_stat_statements 中查找类似这样的模式:
-- This query appearing 500 times in a single page load = N+1 problem
SELECT * FROM res_partner WHERE id = $1
修复方法是使用 Odoo 的 prefetch_ids 机制或将 select 添加到 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
PostgreSQL 配置调优
PostgreSQL 附带了保守的默认值,旨在在任何硬件上运行。对于 Odoo 生产服务器,必须调整这些默认值。
内存设置(对于 32 GB 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
连接设置:
# Maximum connections (Odoo workers × 2 + headroom)
max_connections = 200
# For connection pooling with PgBouncer
# If using PgBouncer, reduce to 50-100
查询计划器设置:
# 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 调整:
Odoo 的库存、会计和消息传递模块会产生大量 INSERT/UPDATE 流量。默认 autovacuum 设置不够:
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
关键数据库索引
缺少索引是 Odoo 性能问题的第二大常见原因,仅次于配置不当。 Odoo 为主键和一些外键创建索引,但许多常用过滤字段缺少索引。
使用 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;
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 配置
Odoo 工作线程的数量决定了服务器可以处理的并发请求数。
工人计数公式:
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 工作进程设置:
[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
了解工人类型:
Odoo 使用两种类型的工作人员:
- HTTP工作人员(
workers配置):处理所有网络请求 - Cron工人(保留1个):在后台运行计划的操作
cron 工作线程始终运行,但不计入您的 HTTP 容量。确保即使在峰值负载下也至少有 1 个 cron 工作线程可用。
Nginx 性能配置
Nginx 位于 Odoo 前面,负责处理 TLS 终止、静态文件服务和可选的缓存。
高性能Nginx配置:
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 用于会话和缓存
Redis 显着降低了会话管理和 Odoo ORM 缓存的数据库负载。
安装和配置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
配置 Odoo 使用 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
监控和持续绩效管理
设置pgBadger进行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
要监控的关键指标:
| 公制 | 警告阈值 | 临界阈值 |
|---|---|---|
| 页面加载时间 | > 2 秒 | > 5 秒 |
| 数据库查询时间 | > 平均 100 毫秒 | > 平均 500 毫秒 |
| 工人记忆 | > 限制的 80% | > 极限值的 95% |
| PostgreSQL 连接 | > 最大值的 70% | > 最大值的 90% |
| 磁盘 IOPS | > 80% 的配置 | > 95% 的配置 |
| 缓存命中率 | < 95% | < 90% |
常见问题
50 个并发 Odoo 用户的最低服务器规格是多少?
对于具有中等交易量的 50 个并发用户:8 个 vCPU、32 GB RAM、500 GB SSD(首选 NVMe)。 Odoo 的 PostgreSQL 数据库是主要的 I/O 瓶颈,因此快速存储比原始 CPU 速度更重要。配置 13 个工作线程(8×2-3 的预留空间)、8 GB 共享缓冲区,并确保您的数据库位于 SSD 卷上。
如何诊断我的 Odoo 慢是 Python 还是 PostgreSQL?
使用 Odoo 的内置分析器(开发人员模式下的设置 → 技术 → 性能 → 分析器)来记录缓慢的操作。火焰图将显示时间是花费在 Python 代码上还是等待 SQL 结果。如果 SQL 查询占主导地位,请重点关注 PostgreSQL 调优和索引。如果 Python 占主导地位,请查找是否缺少 @api.depends 缓存或自定义代码效率低下。
我应该使用 PgBouncer 进行连接池吗?
是的,适用于具有 30 多个 Odoo 工作人员或大量 API 流量的部署。事务模式池中的 PgBouncer 允许许多 Odoo 工作人员共享较小的实际 PostgreSQL 连接池,从而减少每个连接的开销。使用 PgBouncer 时,将 PostgreSQL 中的 max_connections 配置为 50-100,然后设置 PgBouncer 的 pool_size 以匹配您的 Odoo 工作线程数。
我应该多久在 Odoo 数据库上运行 VACUUM ANALYZE 一次?
如果配置正确,Autovacuum 会自动处理此问题。经过上述调整(激进的比例因子,更多的工作人员)后,autovacuum 应该在活动表上连续运行。运行 SELECT schemaname, tablename, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 20; 来验证表是否经常被清理。
Odoo 工作人员过多会产生什么影响?
每个 Odoo 工作线程至少消耗 256–512 MB RAM。太多的worker会导致内存耗尽,导致worker崩溃(limit_memory_hard),从而导致用户出现HTTP 500错误。此外,太多的 PostgreSQL 连接(workers × max_db_connections)可能会压垮数据库。从公式(CPU×2+1)开始,监视负载下的内存,并根据需要向下调整。
后续步骤
Odoo 性能调优是一个迭代过程。单个调整会话可以带来显着的收益,但持续的性能需要随着数据量的增长进行持续监控、定期索引分析和配置调整。
ECOSIRE 为企业部署提供 Odoo 性能审计,为您的特定工作负载、事务模式和基础设施确定影响最大的优化。我们的工程师已将 Odoo 安装从 10 个用户的中小型企业部署调整为 500 个用户的企业部署。
分享您当前的服务器规格、用户数量以及您遇到的症状,我们的团队将确定根本原因并提供优先优化计划。
作者
ECOSIRE Research and Development Team
在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。
相关文章
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.
更多来自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.