如何在不改SQL的情況下優化數據庫

主題簡介

在數據庫運維中我們會遇到各種各樣的問題,這些問題的根源可能很明顯,也可能被某種表象掩蓋而使我們認不清。所以運維面臨的兩大問題就是,第一我們沒有看清本質,第二應用不允許修改。那麼我們如何解決這兩個問題,是每一個運維者都應該思考的。今晚的分享將會從三個方面來進行。如何準確定位問題,如何不修改應用進行優化,以及如何通過SQL審覈防患未然


大家好。我是雲和恩墨公司的專家羅海雄,主要專長於性能優化這個領域,包括數據庫的優化,SQL優化等。今天給大家分享的主題是“如何在無法修改應用時進行數據庫優化”。


我先從一個真實的案例說起。我們的一個客戶,是一個汽車經銷商,他們的財務系統出現了嚴重的性能問題。  


這是他們的CPU壓力的示意圖。



紅色的是CPU使用量,綠色的是CPU空閒,黑色的是IO等待。我們可以看到,在圖的中間部分,也就是月底的時候,CPU使用量已經達到80-90%, 而CPU Idle 已經接近0了。

 

中間似乎還有一段CPU 空閒100%, 其實是由於數據庫壓力太大,主機自動重啓了。 這種情況已經嚴重影響他們公司財務系統的月結工作。他們趕緊找到我們公司,我一看AWR, 發現大量SQL執行時間很長。



從表裏可以看到,第一個SQL平均一次執行需要2700秒,後面還有幾個SQL單次執行在2000秒以上,顯然有很大問題。

 

發現明顯有部分SQL寫法上存在問題,正是這些SQL,導致數據庫壓力過大。

其中一條SQL是這個樣子的:


幾個表都是幾千萬條記錄的大表,re.FSrcEntryId是個選擇都很高的欄位,sie.FID是個主鍵, si.FId 也是主鍵。

T_IM_SaleIssueEntry是外連接的驅動表,但是上面沒有直接的條件。

T_IM_SaleIssueBill 和 T_BOT_RELATIONENTRY都是被驅動表,上面有兩個條件:


si.FTransactionTypeIDIN (:3, :4)re.FSrcEntryId IN (:5, :6)


這種情況下,由於被驅動表已經有了確定值,邏輯上來說,外連接和內連接是等價的,但是Oracle 10g 的優化器沒有爲這種情況做優化,導致在優化前,走了大表的全表掃描



這一類SQL不少。我們最早的建議就是修改部分寫法存在問題的SQL, 從源頭入手,嘗試解決。比如說,把LEFT OUTER JOIN 改成普通JOIN, 邏輯上是一樣的,就能走剛纔的好的執行計劃。



但是,應用開發廠商表示,由於這個版本比較老,他們已經沒有專門的人員對代碼進行維護,無法修改SQL。

 

這就是作爲一個系統維護DBA,經常會碰到的問題。


系統剛上線,一切很美好...

一兩年後,由於數據量的積累,用戶數增多,功能點使用增多等原因,會使負荷逐漸增加,從而出現性能問題。根據我們的經驗,在這些性能問題裏面,SQL的問題可能佔了80%.但是,由於種種原因,可能就是沒法修改SQL.


比如說:

-- 使用封裝好的商業套件

-- 外包開發,開發商已經離場

-- 自行開發,但開放部門不願意配合進行代碼修改

 

這就到了今天的主題:“如何在無法修改應用時進行數據庫優化

總的來說,可以從硬件和軟件兩個方面去解決:硬件方面,可以通過增加或者CPU,增加內存,做一定的改善。也可以通過升級成增加RAC, 也可以增加CPU的處理能力。存儲方面,升級更好地存儲,針對一些I/O要求比較高的系統,也一種優化的手段。




這是我們的一個客戶,通過把存儲介質升級到PCIE Flash卡,極大的改善了I/O性能問題,是整個系統的性能得到了極大的提升。

 

我們公司的zData產品,通過高速的PCIE Flash卡,結合超大帶寬,超小延遲的高速IB網絡, 也可以非常有效地提高I/O的性能,總體性能是傳統存儲的10倍以上。這樣,在不修改任何SQL的情況下,就輕鬆解決了I/O的瓶頸,提高的系統的性能。當然,對於不差錢的單位/企業,使用Oracle的Exadata也是一種方法;很簡便的、不修改應用就能明顯提高系統性能的方法。動硬件往往涉及到預算。不增加開銷的情況下,也可以通過數據庫的層面做一些優化。

 

回到我一開始介紹的那個案例。

經過研究,我們發現,LEFT OUTER JOIN不能等價轉換成普通JOIN是Oracle 10g的行爲模式。在Oracle 11g中,優化器做了升級,能夠識別並內部進行這種轉換。而恰巧,用戶用的是Oracle 11g的數據庫,只是由於應用開發方的要求,把優化器模式設爲了10.2.0.1.

最終,通過和應用開發方,使用方的多次溝通和測試,最終把優化器模式設爲了11.2.0, 從而解決了最大的問題。


當然,LEFT OUTER JOIN只是其中一個問題,後來,在這個客戶的數據庫上,我們還有針對性的建立了100多個索引。最終,在沒有修改任何SQL的情況下,徹底解決了用戶的系統性能問題。


通常來說,數據庫層面的優化包括

參數調整:內存參數,優化器參數等

表結構調整:索引, 並行度,分區

SQL執行計劃調整:SQL Profile,SPM,SQL Patch

其它: Cache 表、統計信息、物化視圖+查詢重寫、數據歸檔等等

 

合適的優化器參數,會使你係統索引的問題看起來很簡單,但往往是最有效的方法之一。大家討論的比較多,我就不深入了。分區以及數據歸檔也是一個常用的手段。實際上,數據都是有生命週期的。很多用戶的數據庫裏面,存了很多已經不需要的數據,通過清理、歸檔這些數據,往往也能獲得比較高的優化效果。


還有一些情況,SQL寫的並沒有問題,但由於種種原因,數據庫經常走錯執行計劃;這時候,通過改寫SQL, 增加Hint是一種常見解決方式。在無法修改SQL的情況下,也可以通過一些手段對SQL執行計劃進行固定。


這些手段主要包括有:

SQL Profile(Oracle 10g以後)

SQL Plan Baseline Management(Oracle 11g以後)

SQL Patch

Outline

 

SQL profile在Oracle 10g引入

通過爲特定的SQL文本指定優化器的一些信息,從而引導優化器生成更爲合理的SQL執行計劃。達到不修改SQL文本就可以改變並指定執行計劃的目的。

SQL-Profile 主要通過dbms_sqltune包進行控制。時間關係,今天就不爲大家演示了。


SQL Plan Management在Oracle 11g引入

通過爲特定的SQL指定已知SQL執行計劃,強制優化器選擇已經指定的SQL執行計劃,從而達到不修改SQL文本即可修改執行計劃的目的。

可以指定多個可用執行計劃供優化器選擇。

可以和SQL Tuning Advisor一起用

可以自動收集運行庫中SQL 作爲已知執行計劃。

也可以手工設置。


SQL Plan Management主要通過DBMS_SPM包進行控制,SQL Patch是一種強行給SQL加Hint的方法,主要通過sys.dbms_sqldiag_internal.i_create_patch進行。



不同的數據庫優化方式對整個數據庫影響面各有不同,在使用的時候,需要謹慎的程度也不一樣。


經典問題分享

問題一

關於SQL Profile使用方法有什麼好的推薦的書籍或者文檔介紹之類的

關於 SQL profile,推薦老熊的兩篇文章  

http://www.laoxiong.net/sql-profiles-part.html

http://www.laoxiong.net/sql-profiles-partii.html

 

問題二

關於驅動表和被驅動表如何確定?

對於Nested loop來說,被驅動表需要有高選擇度索引,驅動表的結果應該儘量小。 Hash Join沒有被驅動表需要索引的問題,只剩下驅動表結果集小的需求。多數情況,能夠迅速幫你把數據量減下來的表,適合當驅動表。

 

問題三

同一條sql的執行計劃經常會變是什麼問題?

統計信息以及綁定變量是變化的主因

Oracle的新特性 基數反饋和adaptive cursor 也會造成執行計劃不穩定

 

問題四

諮詢一下zdata分佈式存儲採用的是什麼raid方式?

zdata分佈式存儲採用的是分散的mirror機制。每個塊至少在兩個主機的Flash上存放,同時有機制保證某個主機出現問題後,把相應的塊重新分佈到其他主機上。

 

問題五

很多文章都在說索引高度太大會增加io開銷,在數據量日漸增多的情況下,如何降低索引高度呢? 

分區是一個辦法

不是特別建議通過重建的方法強行降blevel, 在反彈的時候容易造成索引分裂,影響系統性能

 

問題六

今天我們優化sql上午時候,表關聯,使用了jion,每個表都走了索引,但是從執行計劃上看消耗掉的cpu還是很多,沒有明顯的下降。想問一下。優化sql需要考慮到具體方面

雖然走索引,當時如果驅動表結果集較大,多次的索引掃描,同樣會導致性能不好。

總的來說,訪問越少的Block, 性能就越好。合適的條件儘可能放在執行計劃鏈的前段,迅速把結果集圈定在最終需要的結果集裏。

如果缺乏合適的過濾條件,那麼,考慮用Hash Join代替nested loop. 當然,還得看實際的情況。

 

問題七

動態生成的sql如何進行優化

 “動態生成的sql如何優化”這個問題有點寬泛。我猜測提問者想問的可能根據用戶輸入條件動態拼接而成的SQL如何優化。這個問題得具體分析。通常來說,儘量把條件限制在第一個表,適當的多建索引,以及針對用戶實際行爲進行鍼對性優化。

比如說,在我服務過的一個公司,系統裏有個界面,操作人員可以根據需要選擇不同的條件,這些條件組合來自於多個表。

後來,我們直接分析系統訪問日誌,發現80%的查詢都是特定兩個表的條件,分別位於from 列表的第一個和第三個表。而第二個表是個關係表。

於是,我採用了比較少見的處理方式,把第一個表和第三個表的關鍵字段組合起來,創建了一個新的表,並在這個表的相應字段創建索引,而這個表的數據通過trigger的方式進行同步。最終,相關的SQL性能提升數百倍,從30秒鐘下降到100ms。系統總體CPU降低 50%以上。

問題八

什麼時候數據庫會走錯誤的執行計劃啊?

Oracle的新特性 基數反饋和adaptive cursor 也會造成執行計劃不穩定

 

問題九

trigger能保證事務嗎?

可以

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章