在日常數據庫運維過程中,經常會遇到這樣一類問題:
數據庫整體性能正常,但某一條 SQL 在業務系統中執行極慢,
然而 DBA 在后臺手工執行時,卻發現 執行速度非常快。
這類問題往往不在“數據庫整體性能”,而隱藏在具體 SQL 的執行細節和業務執行環境差異中。
本文記錄了一次真實的 SQL 性能排查過程,最終定位到 臨時表使用不當 所引發的問題。
一、問題現象:整體正常,單條 SQL 異常
客戶業務系統運行過程中,出現如下現象:
? 數據庫整體運行狀態正常
? CPU、IO、等待事件均無明顯異常
? 僅有 一條業務 SQL 在前臺執行非常緩慢
? 同一條 SQL 在后臺手工執行時,執行速度卻非常快
從表象來看,這并不像是數據庫整體性能瓶頸,更像是某條 SQL 在特定執行場景下存在問題。
二、通過 AWR 報告定位性能熱點
為進一步確認問題,通過 AWR 報告(使用 @?/rdbms/admin/awrrpti.sql 生成)對系統性能進行分析。

AWR 報告顯示:
? 系統的主要性能消耗集中在 一條 SQL
? 該 SQL 單次執行時間約為 406,814 ms
從執行時間來看,這條 SQL 明顯是當前系統的主要性能瓶頸。

三、執行計劃“完美”,卻解釋不了慢的問題
繼續查看該 SQL 的執行計劃,發現一個容易讓人放松警惕的情況:
? 執行計劃 cost 不大
? 各個執行步驟看起來都非常合理
? 沒有明顯的全表掃描或低效訪問路徑
從執行計劃角度判斷,這條 SQL 理論上不應該這么慢。

但事實是:

SQL 確實執行得非常慢。
四、執行統計信息暴露異常:buffer gets 極高
在進一步查看 SQL 的執行統計信息后,問題開始顯現出來。
該 SQL 的統計信息顯示:
? buffer gets 達到 595,764,242
? 這是一個極不正常的數值
而從數據規模來看:
? SQL 中涉及的最大表 GL_VERIFY_LOG 只有 17,710 行
? 其他表的數據量更是可以忽略不計
在如此小的數據規模下,卻出現如此高的 buffer gets,顯然不符合正常邏輯。
五、常規思路排查:統計信息并非根因
第一反應自然是懷疑:
是否因為對象統計信息不準確,導致優化器判斷失誤?
于是對 SQL 涉及的所有對象進行了 100% 采樣的統計信息收集。
但結果是:
? SQL 執行性能沒有任何改善
? buffer gets 依然很高
同時可以確認:
? SQL 未使用綁定變量
? 不存在 bind peeking 導致的執行計劃不穩定問題
? 后臺執行時顯示的執行計劃,與 AWR 中的執行計劃一致
此時,執行計劃、統計信息、SQL 寫法,似乎都“沒有問題”。
六、關鍵細節:執行計劃中的 Dynamic Sampling
在反復對比前臺與后臺執行差異時,注意到一個細節:
后臺執行該 SQL 時,執行計劃中出現了 Dynamic Sampling(動態采樣)。

這是一個非常關鍵的信號。
在 Oracle 中,涉及臨時表的 SQL,往往會觸發動態采樣,以便在運行時獲取更準確的數據分布信息。
這一現象直接將排查方向指向了 SQL 中可能存在的臨時表。
七、問題根因確認:臨時表數據量差異
繼續深入檢查 SQL 涉及的對象,最終確認:
? SQL 中使用了臨時表 ASSTEMPORA
? 前臺業務程序執行 SQL 前,會向該臨時表中加載 大量數據
? 后臺 DBA 手工執行 SQL 時,該臨時表 數據為空
這就解釋了所有現象:
? 前臺執行:
? 臨時表數據量大
? SQL 執行過程中產生大量 buffer gets
? 導致執行時間極長
? 后臺執行:
? 臨時表無數據
? SQL 執行路徑看起來“非常理想”
? 執行時間極短
八、優化方案:為臨時表補充索引
問題的本質在于:
臨時表在高數據量參與 SQL 計算時,缺失必要索引。
針對該問題,在臨時表 ASSTEMPORA 上創建索引:
CREATE INDEX ASSTEMPORA_IDX ON ASSTEMPORA (assid);
索引創建完成后:
? 前臺 SQL 執行時間明顯縮短
? buffer gets 大幅下降
? 業務模塊性能恢復正常
問題得到徹底解決。
九、小結:臨時表同樣需要“正式對待”
通過這次問題排查,可以得到幾個非常重要的經驗結論:
1. 執行計劃看起來沒問題,并不代表 SQL 一定高效
2. 前臺與后臺執行差異,往往來自 數據狀態不同
3. 臨時表一旦承載真實業務數據,就必須像普通表一樣設計索引
4. 執行計劃中的 Dynamic Sampling 是一個非常重要的排查線索
很多 SQL 性能問題,并不是寫得復雜,而是在真實業務場景下,被放大了原本被忽略的設計缺陷。




