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 मार्च 202610 मिनट पढ़ें2.3k शब्द|

हमारी Performance & Scalability श्रृंखला का हिस्सा

पूरी गाइड पढ़ें

Odoo प्रदर्शन ट्यूनिंग: PostgreSQL और सर्वर अनुकूलन

धीमे Odoo उदाहरण की वजह से उत्पादकता में कमी आती है और उपयोगकर्ता निराश होते हैं। अच्छी खबर: अधिकांश Odoo प्रदर्शन समस्याएँ हार्डवेयर अपग्रेड के बिना हल की जा सकती हैं। बुरी खबर: मूल कारण का निदान करने के लिए संपूर्ण स्टैक - पायथन, पोस्टग्रेएसक्यूएल, नेग्नेक्स, रेडिस और नेटवर्क परत को समझने की आवश्यकता है।

यह मार्गदर्शिका Odoo 19 एंटरप्राइज के लिए पूर्ण प्रदर्शन अनुकूलन जीवनचक्र को कवर करती है: बाधाओं की पहचान करना, PostgreSQL को ट्यून करना, Odoo सर्वर सेटिंग्स को अनुकूलित करना, Nginx कैशिंग को कॉन्फ़िगर करना, और आपके उपयोगकर्ता संख्या और लेनदेन की मात्रा के लिए आपके बुनियादी ढांचे को सही आकार देना।

मुख्य बातें

  • PostgreSQL ट्यूनिंग सबसे बड़ा प्रदर्शन लाभ प्रदान करती है (सामान्य इंस्टॉलेशन में 50-300%)
  • शुरुआती बिंदु के रूप में साझा बफ़र्स को उपलब्ध रैम के 25% पर सेट किया जाना चाहिए
  • Odoo का ORM N+1 क्वेरीज़ उत्पन्न करता है जिन्हें pg_stat_statements से पकड़ा जा सकता है
  • बार-बार फ़िल्टर किए गए फ़ील्ड (कंपनी_आईडी, राज्य, दिनांक) पर अनुक्रमणिका अनिवार्य हैं
  • Nginx प्रॉक्सी कैशिंग 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 प्रश्नों की पहचान करें:

एन+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 तंत्र का उपयोग करें या अपनी ORM क्वेरी में select जोड़ें:

# 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 जीबी रैम सर्वर के लिए):

# /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

हाई-राइट वर्कलोड के लिए ऑटोवैक्यूम ट्यूनिंग:

Odoo की इन्वेंट्री, अकाउंटिंग और मैसेजिंग मॉड्यूल भारी INSERT/UPDATE ट्रैफ़िक उत्पन्न करते हैं। डिफ़ॉल्ट ऑटोवैक्यूम सेटिंग्स अपर्याप्त हैं:

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

क्रिटिकल डेटाबेस इंडेक्स

खराब कॉन्फ़िगरेशन के बाद ओडू प्रदर्शन समस्याओं का दूसरा सबसे आम कारण गुम अनुक्रमणिका है। ओडू प्राथमिक कुंजी और कुछ विदेशी कुंजी के लिए इंडेक्स बनाता है, लेकिन कई सामान्य रूप से फ़िल्टर किए गए फ़ील्ड में इंडेक्स की कमी होती है।

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;

ओडू 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 कार्यकर्ताओं की संख्या यह निर्धारित करती है कि सर्वर कितने समवर्ती अनुरोधों को संभाल सकता है।

कर्मचारी गणना का सूत्र:

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

कर्मचारी प्रकारों को समझना:

ओडू दो प्रकार के श्रमिकों का उपयोग करता है:

  1. HTTP कार्यकर्ता (workers कॉन्फ़िगरेशन): सभी वेब अनुरोधों को संभालें
  2. क्रॉन वर्कर्स (1 आरक्षित): पृष्ठभूमि में निर्धारित क्रियाएँ चलाएँ

क्रॉन वर्कर हमेशा चालू रहता है लेकिन आपकी HTTP क्षमता में नहीं गिना जाता है। सुनिश्चित करें कि चरम भार पर भी कम से कम 1 क्रॉन कर्मचारी उपलब्ध हो।


प्रदर्शन के लिए 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 सत्र प्रबंधन और Odoo के ORM कैश के लिए डेटाबेस लोड को काफी कम कर देता है।

रेडिस स्थापित और कॉन्फ़िगर करें:

# 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.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

निगरानी और चल रहे प्रदर्शन प्रबंधन

** PostgreSQL लॉग विश्लेषण के लिए pgBadger सेट करें:**

# 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 सेकंड
डीबी क्वेरी समय> 100ms औसत> 500ms औसत
कार्यकर्ता स्मृति> सीमा का 80%> 95% सीमा
PostgreSQL कनेक्शन> अधिकतम 70%> अधिकतम 90%
डिस्क IOPS> प्रावधान का 80%> 95% प्रावधान
कैश हिट अनुपात<95%<90%

अक्सर पूछे जाने वाले प्रश्न

50 समवर्ती ओडू उपयोगकर्ताओं के लिए न्यूनतम सर्वर विनिर्देश क्या है?

मध्यम लेनदेन मात्रा वाले 50 समवर्ती उपयोगकर्ताओं के लिए: 8 वीसीपीयू, 32 जीबी रैम, 500 जीबी एसएसडी (एनवीएमई पसंदीदा)। Odoo का PostgreSQL डेटाबेस प्राथमिक I/O बाधा है, इसलिए तेज़ स्टोरेज कच्चे CPU गति से अधिक मायने रखता है। 13 कर्मचारियों को कॉन्फ़िगर करें (हेडरूम के लिए 8×2-3), 8 जीबी साझा_बफ़र्स, और सुनिश्चित करें कि आपका डेटाबेस एसएसडी वॉल्यूम पर है।

मैं कैसे पता लगाऊं कि मेरा ओडू धीमापन पायथन है या पोस्टग्रेएसक्यूएल?

धीमे ऑपरेशन को रिकॉर्ड करने के लिए ओडू के अंतर्निर्मित प्रोफाइलर (सेटिंग्स → तकनीकी → प्रदर्शन → डेवलपर मोड में प्रोफाइलर) का उपयोग करें। फ्लेमग्राफ दिखाएगा कि क्या समय पायथन कोड में बिताया गया है या SQL परिणामों की प्रतीक्षा में बिताया गया है। यदि SQL क्वेरीज़ हावी हैं, तो PostgreSQL ट्यूनिंग और इंडेक्स पर ध्यान केंद्रित करें। यदि पायथन हावी है, तो लापता @api.depends कैशिंग या कस्टम कोड अक्षमताओं को देखें।

क्या मुझे कनेक्शन पूलिंग के लिए PgBouncer का उपयोग करना चाहिए?

हाँ, 30+ Odoo कार्यकर्ताओं या भारी API ट्रैफ़िक वाली तैनाती के लिए। लेन-देन-मोड पूलिंग में PgBouncer कई Odoo श्रमिकों को वास्तविक PostgreSQL कनेक्शन का एक छोटा पूल साझा करने की अनुमति देता है, जिससे प्रति-कनेक्शन ओवरहेड कम हो जाता है। PgBouncer का उपयोग करते समय PostgreSQL में max_connections को 50-100 पर कॉन्फ़िगर करें, फिर अपने Odoo वर्कर गिनती से मेल खाने के लिए PgBouncer का पूल_साइज़ सेट करें।

मुझे Odoo डेटाबेस पर कितनी बार VACUUM ANALYZE चलाना चाहिए?

यदि सही ढंग से कॉन्फ़िगर किया गया है तो ऑटोवैक्यूम इसे स्वचालित रूप से संभालता है। उपरोक्त ट्यूनिंग (आक्रामक पैमाने के कारक, अधिक श्रमिक) के बाद, ऑटोवैक्यूम को सक्रिय तालिकाओं पर लगातार चलना चाहिए। यह सत्यापित करने के लिए 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 कार्यकर्ताओं का क्या प्रभाव पड़ता है?

प्रत्येक ओडू कार्यकर्ता न्यूनतम 256-512 एमबी रैम की खपत करता है। बहुत से श्रमिकों के कारण मेमोरी समाप्त हो जाती है, जिससे श्रमिक क्रैश हो जाते हैं (limit_memory_hard), जिसके परिणामस्वरूप उपयोगकर्ताओं के लिए HTTP 500 त्रुटियाँ होती हैं। इसके अतिरिक्त, बहुत सारे PostgreSQL कनेक्शन (वर्कर्स × max_db_connections) डेटाबेस को प्रभावित कर सकते हैं। सूत्र (सीपीयू×2+1) से शुरू करें, लोड के तहत मेमोरी की निगरानी करें, और यदि आवश्यक हो तो समायोजित करें।


अगले चरण

ओडू प्रदर्शन ट्यूनिंग एक पुनरावृत्तीय प्रक्रिया है। एक एकल ट्यूनिंग सत्र महत्वपूर्ण लाभ प्रदान करता है, लेकिन निरंतर प्रदर्शन के लिए निरंतर निगरानी, ​​​​आवधिक सूचकांक विश्लेषण और आपके डेटा की मात्रा बढ़ने पर कॉन्फ़िगरेशन समायोजन की आवश्यकता होती है।

ECOSIRE एंटरप्राइज़ परिनियोजन के लिए Odoo प्रदर्शन ऑडिट प्रदान करता है, जो आपके विशिष्ट कार्यभार, लेनदेन पैटर्न और बुनियादी ढांचे के लिए उच्चतम-प्रभाव अनुकूलन की पहचान करता है। हमारे इंजीनियरों ने ओडू इंस्टॉलेशन को 10-उपयोगकर्ता एसएमबी से 500-उपयोगकर्ता एंटरप्राइज़ परिनियोजन तक ट्यून किया है।

ECOSIRE से ओडू प्रदर्शन ऑडिट का अनुरोध करें →

अपने वर्तमान सर्वर विनिर्देश, उपयोगकर्ता संख्या और आपके द्वारा अनुभव किए जा रहे लक्षणों को साझा करें, और हमारी टीम मूल कारणों की पहचान करेगी और प्राथमिकता वाले अनुकूलन योजना प्रदान करेगी।

शेयर करें:
E

लेखक

ECOSIRE Research and Development Team

ECOSIRE में एंटरप्राइज़-ग्रेड डिजिटल उत्पाद बना रहे हैं। Odoo एकीकरण, ई-कॉमर्स ऑटोमेशन, और AI-संचालित व्यावसायिक समाधानों पर अंतर्दृष्टि साझा कर रहे हैं।

WhatsApp पर चैट करें