首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> DB2 資料庫 >> DB2 教學

一次 Mondrian 死鎖問題的排查與解決實錄

     編輯:DB2 教學

一次 Mondrian 死鎖問題的排查與解決實錄

項目背景

OLAP 分析系統,分邏輯服務器(元數據)與分析服務器(MDX 查詢執行)。原使用 Mondrian 引擎,存在效能與擴展性瓶頸:緩存進程內部、有狀態、易 OOM。任務是將緩存移至第三方緩存系統。

事故復現

上線後用戶反饋報表打開極慢。回滾版本後問題依然可在測試環境復現:點擊「清除緩存」操作(異步)後,後續操作出現卡頓,最終導致 Tomcat 線程耗盡。

死鎖堆疊分析

使用 jstack 抓取線程堆疊,發現明確死鎖:

Found one Java-level deadlock:
"mondrian.rolap.RolapResultShepherd$executor_160": waiting for monitor 0x... (MemberCacheHelper), held by "executor_152"
"mondrian.rolap.RolapResultShepherd$executor_152": waiting for monitor 0x... (MemberCacheHelper), held by "http-8182-11"
"http-8182-11": waiting for monitor 0x... (MemberCacheHelper), held by "executor_152"

三線程循環等待,大量其他線程阻塞於這兩把鎖,導致線程池耗盡。

代碼定位:flushCache 與 flushCacheSimple 的巢狀鎖

堆疊指向 RolapCubeHierarchy.flushCache → MemberCacheHelper.flushCache → flushCacheSimple。核心問題在 SmartMemberReader 類:

// 子類 flushCache
public void flushCache() {
    super.flushCache();           // 鎖父類 cacheHelper
    rolapCubeCacheHelper.flushCache(); // 鎖子類 cacheHelper
}

// 父類 flushCache
public void flushCache() {
    synchronized(cacheHelper) { cacheHelper.flushCache(); }
}

// MemberCacheHelper.flushCache
public synchronized void flushCache() {
    // 清理 maps...
    if (rolapHierarchy instanceof RolapCubeHierarchy) {
        ((RolapCubeHierarchy)rolapHierarchy).flushCacheSimple(); // 又回來了!
    }
}

// 子類 flushCacheSimple
public void flushCacheSimple() {
    super.flushCacheSimple();           // 鎖父類 cacheHelper
    rolapCubeCacheHelper.flushCacheSimple(); // 鎖子類 cacheHelper
}

死鎖根因

  • 父類與子類各持有一個 MemberCacheHelper(cacheHelper 與 rolapCubeCacheHelper)。
  • flushCache 呼叫順序:父類鎖 → 子類鎖 → 觸發 flushCacheSimple → 子類鎖 → 父類鎖。
  • flushCacheSimple 呼叫順序:子類鎖 → 父類鎖。
  • 線程 A 執行 flushCache 持有父鎖等子鎖;線程 B 執行 flushCacheSimple 持有子鎖等父鎖 → 經典死鎖。

解決方案

經分析,flushCache 與 flushCacheSimple 邏輯高度重複(均清理相同 maps)。決定移除所有對 flushCacheSimple 的呼叫,打破巢狀鎖循環。熱修復上線後問題解決。

心得與反思

  1. 善用 jstack/jmap/MAT 等工具定位問題。
  2. 面對遺留代碼不推卸責任,靜心分析邏輯流程。
  3. 測試至關重要,特別是壓力測試,需覆蓋線上並發場景。
  4. 上線前務必準備回滾方案。
  5. 多鎖場景嚴格遵循「固定順序加鎖」原則。
  6. 釐清方法職責:輸入、輸出、副作用,腦中預演執行流。
  7. 父子類共享同類型輔助物件時,避免交織呼叫,分階段獨立操作。

後續規劃

深入研究 Mondrian:MDX 解析、動態 SQL 生成、緩存實現、聚合表優化。需補強編譯原理知識。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved