【進階維運】iDempiere 效能優化與 Partition Table:DBA 完全攻略

iDempiere 效能優化與 Partition Table:DBA 完全攻略

iDempiere 運行久了,資料量增長會影響效能。本文從 DBA 角度分析效能優化策略,包含 Partition Table、索引設計、慢查詢分析與備份計畫。

一、iDempiere 資料庫特性

表數量眾多

iDempiere 有數百張表,主要分類:

  • AD_* 表:Application Dictionary,存系統定義
  • C_* 表:Commercial(銷售、客戶、訂單)
  • M_* 表:Material(產品、庫存、倉庫)
  • A_* 表:Asset(資產)
  • Fact_Acct:會計分錄(成長最快)

快速成長的表

表名 成長原因
AD_PInstance / AD_PInstance_Para 每次執行 Process 都會新增
AD_ChangeLog 資料異動記錄
Fact_Acct 會計分錄
AD_Attachment 上傳檔案(BLOB)

二、Partition Table

iDempiere 原生支援 Partition Table,可從 UI 操作!

查詢已設定分區的表

SELECT TableName, IsPartition 
FROM AD_Table 
WHERE IsPartition = 'Y';

適合分區的表

  • AD_PInstance:依 Created 日期分區
  • Fact_Acct:依 DateAcct 分區
  • AD_ChangeLog:依 Created 分區

分區優點

  • 查詢只掃描相關分區,效能提升
  • 可獨立備份/刪除舊分區
  • 維護操作(VACUUM)更快

三、索引策略

常用查詢欄位建索引

-- 訂單表常用查詢
CREATE INDEX idx_c_order_bpartner ON C_Order(C_BPartner_ID);
CREATE INDEX idx_c_order_docstatus ON C_Order(DocStatus);
CREATE INDEX idx_c_order_date ON C_Order(DateOrdered);

-- 複合索引
CREATE INDEX idx_c_order_multi 
ON C_Order(AD_Client_ID, AD_Org_ID, DocStatus, DateOrdered);

檢查缺少索引的 Foreign Key

SELECT 
    tc.table_name, 
    kcu.column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu 
    ON tc.constraint_name = kcu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND NOT EXISTS (
    SELECT 1 FROM pg_indexes 
    WHERE tablename = tc.table_name 
    AND indexdef LIKE '%' || kcu.column_name || '%'
);

四、慢查詢分析

啟用 pg_stat_statements

-- postgresql.conf
shared_preload_libraries = 'pg_stat_statements'

-- 查詢最慢的 SQL
SELECT 
    query,
    calls,
    total_time / calls as avg_time,
    rows / calls as avg_rows
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 20;

常見效能問題

問題 解決方案
全表掃描 加索引、檢查 WHERE 條件
大量 JOIN 檢查是否必要、考慮反正規化
BLOB 查詢慢 將 Attachment 移到外部儲存
鎖競爭 縮短交易時間、調整隔離等級

五、Stored Procedure 設計

SP 參數取得(透過 PInstance)

CREATE OR REPLACE FUNCTION my_process(pinstance numeric)
RETURNS void AS $$
DECLARE
    p_Record_ID NUMERIC;
BEGIN
    -- 取得參數
    SELECT pi.record_id INTO p_Record_ID
    FROM ad_pinstance pi
    WHERE pi.ad_pinstance_id = pinstance;
    
    -- 執行業務邏輯
    UPDATE my_table SET processed = 'Y'
    WHERE my_table_id = p_Record_ID;
    
    -- 回寫成功狀態
    UPDATE ad_pinstance
    SET isprocessing = 'N',
        result = 1,
        errormsg = 'OK',
        updated = now()
    WHERE ad_pinstance_id = pinstance;
    
EXCEPTION WHEN OTHERS THEN
    -- 錯誤處理
    UPDATE ad_pinstance
    SET isprocessing = 'N',
        result = 0,
        errormsg = SQLERRM,
        updated = now()
    WHERE ad_pinstance_id = pinstance;
END;
$$ LANGUAGE plpgsql;

六、備份策略

完整備份

pg_dump -h localhost -U adempiere idempiere > backup_full.sql

分區表備份

# 只備份特定分區
pg_dump -t 'fact_acct_2024*' idempiere > backup_fact_2024.sql

冷熱資料分離

  • 熱資料:當年度,保留在主資料庫
  • 冷資料:歷史年度,備份後可刪除或移至歸檔庫

七、監控建議

  • 定期檢查表大小:SELECT pg_size_pretty(pg_total_relation_size('table_name'))
  • 監控連線數:SELECT count(*) FROM pg_stat_activity
  • 設定慢查詢 log:log_min_duration_statement = 1000(1秒以上)
  • 定期 VACUUM ANALYZE

常見問題 FAQ

Q1: AD_PInstance 表太大可以清嗎?

可以清除舊資料,但建議保留近期的(如 30 天內),方便追蹤 Process 執行記錄。

Q2: 如何知道哪些表最大?

SELECT tablename, pg_size_pretty(pg_total_relation_size(tablename::text))
FROM pg_tables WHERE schemaname = 'adempiere'
ORDER BY pg_total_relation_size(tablename::text) DESC
LIMIT 20;

Q3: Partition Table 可以從 UI 設定嗎?

可以!在 Table and Column 視窗勾選 IsPartition,並設定分區欄位。

Q4: 多久做一次 VACUUM?

PostgreSQL 預設有 autovacuum,但高負載系統建議每天手動執行 VACUUM ANALYZE

Q5: 備份要包含 AD_Attachment 嗎?

這張表存上傳檔案(BLOB),可能很大。建議評估是否將附件移到外部儲存(如 S3),減輕資料庫負擔。

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *