程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> SQL語句優化DB2應用程序性能

SQL語句優化DB2應用程序性能

編輯:DB2教程

當我們設計一個新的或分析一個現存的系統時,其中所要考慮的一個重要問題就是應用程序的設計問題。即使數據庫設計得很好而且還經過優化處理,應用程序設計不適當還是會引起性能問題的 數據庫。實踐證明,如果應用程序存在設計上的問題,那麼修改這些問題比調整數據庫配置參數更能改善應用程序的性能。

例如,SQL是一種高級語言,具有很大的靈活性,從數據庫中提取相同的數據可以用不同形式的SELECT語句來實現,但是,應用程序的性能卻隨著SELECT語句形式的不同而大相徑庭,這是因為不同形式的SELECT語句具有不同的處理成本。在這種情況下,我們就應該選擇那些處理成本低廉的SELECT語句,這樣,應用程序才會有較好的性能。

DB2通用數據庫本身提供一個SQL編譯器,該編譯器創建編譯後的SQL語句,當該編譯器編譯SQL語句時,它將重新編寫這些SQL語句,以生成一種更容易對其進行優化的形式,這個過程稱之為“查詢重寫(query rewrite)”。

然後,SQL編譯器產生許多滿足用戶查詢要求的、可選的執行方案,並根據表、索引、列和函數的統計數字來評估每個方案的執行成本,最後,從中選取執行成本最低的方案,該過程稱之為“查詢優化(query optimization)”。

有一點很重要,需要我們注意,那就是不管存取方案的優劣,SQL編譯器(包括查詢重寫和優化兩個階段)必須從中選擇一個,以產生滿足應用程序查詢要求的結果集,因此,我們在編寫查詢代碼時,只應查詢我們需要的數據,不需要的數據就不要查詢,這樣做的目的是確保SQL編譯器能夠選擇一個最好的存取方案。

編寫SELECT語句時一般遵循以下七個方面的准則:

1、在SELECT列表中僅僅指明需要的列

我們在編寫SELECT語句時,盡管有時候不需要用到表中所有的字段,但還是習慣用*(表示引用表中所有的字段)來指定表中所有的列,這樣做在編程上確實很簡單、方便,但這麼做的後果是應用程序返回一些我們不需要的列,系統做一些不必要的處理,做一些無用功,徒耗系統寶貴的軟、硬件資源,尤其當表中有很多字段時,這種浪費現象就越加明顯;而且,這也不是良好的編程習慣,我們不應提倡。

2、使用謂詞來限制返回的行數

在SQL編程語言中,按照評估過程中如何使用謂詞、何時使用謂詞,我們將謂詞劃分為四大類(這四類謂詞各自有不同的處理成本),按性能由高至低排列如下:

范圍界定謂詞

索引參數謂詞

數據參數謂詞

剩余謂詞

范圍界定謂詞是指那些限定索引掃描范圍的謂詞,它們為索引搜索提供鍵值的起始值和/或終止值。索引參數謂詞不用於界定搜索范圍,但可以根據索引對它進行評估,因為謂詞中的列是索引中的一部分。例如,假設表staff中的索引定義在name,dept和years三個字段上,執行下面的SELECT語句: 

SELECTname,job,salaryFROMstaff
WHEREname=’John’
dept=10
years>5

頭兩個謂詞(name=’John’和dept=10)是范圍界定謂詞,而years>5是索引參數謂詞,因為單憑上述信息我們無法確定鍵years的起始值是多少,起始值可以是6,8,10,甚至更大。如果years的謂詞是years>=5,那麼,它就是范圍界定謂詞了,因為索引搜索可以從5開始。

數據庫管理器在評估這些謂詞的時候將利用索引數據,而不是讀取數據庫中的基本表。這些范圍界定謂詞和索引參數謂詞通過減少需要從表中讀取的行的數目來減少存取的數據頁的數目。索引參數謂詞不影響被存取的索引頁的數目。

數據參數謂詞是那些不能被索引管理器評估,卻能被數據管理服務(DMS)評估的謂詞。通常,這種謂詞需要從基本表中存取個別行,如果需要的話,數據管理服務還會提取需要的列來評估該謂詞。

例如,假設索引定義在表project的projno列上,而不是deptno列上,執行下面的查詢:

SELECTprojno,projname,repempFROMproject
WHEREdeptno=’D11’
ORDERBYprojno

2、使用謂詞來限制返回的行數

在SQL編程語言中,按照評估過程中如何使用謂詞、何時使用謂詞,我們將謂詞劃分為四大類(這四類謂詞各自有不同的處理成本),按性能由高至低排列如下:

范圍界定謂詞

索引參數謂詞

數據參數謂詞

剩余謂詞

范圍界定謂詞是指那些限定索引掃描范圍的謂詞,它們為索引搜索提供鍵值的起始值和/或終止值。索引參數謂詞不用於界定搜索范圍,但可以根據索引對它進行評估,因為謂詞中的列是索引中的一部分。例如,假設表staff中的索引定義在name,dept和years三個字段上,執行下面的SELECT語句: 

SELECTname,job,salaryFROMstaff
WHEREname=’John’
dept=10
years>5

頭兩個謂詞(name=’John’和dept=10)是范圍界定謂詞,而years>5是索引參數謂詞,因為單憑上述信息我們無法確定鍵years的起始值是多少,起始值可以是6,8,10,甚至更大。如果years的謂詞是years>=5,那麼,它就是范圍界定謂詞了,因為索引搜索可以從5開始。

數據庫管理器在評估這些謂詞的時候將利用索引數據,而不是讀取數據庫中的基本表。這些范圍界定謂詞和索引參數謂詞通過減少需要從表中讀取的行的數目來減少存取的數據頁的數目。索引參數謂詞不影響被存取的索引頁的數目。

數據參數謂詞是那些不能被索引管理器評估,卻能被數據管理服務(DMS)評估的謂詞。通常,這種謂詞需要從基本表中存取個別行,如果需要的話,數據管理服務還會提取需要的列來評估該謂詞。

例如,假設索引定義在表project的projno列上,而不是deptno列上,執行下面的查詢:

SELECTprojno,projname,repempFROMproject
WHEREdeptno=’D11’
ORDERBYprojno

指定FOR UPDATE子句的另外一個好處是可以減少應用程序的死鎖機會。死鎖是這樣一種情形:一個以上的應用程序等待另外一個應用程序釋放對數據的鎖定,而那些等待的應用程序正占據著另外應用程序所需要的數據,並且對該數據也進行了鎖定。我們假設兩個應用程序按下列順序同時對同一行進行提取、更新操作:

(1)應用程序1提取該行

(2)應用程序2提取該行

(3)應用程序1更新該行

(4)應用程序2更新該行

在第4步,應用程序2將等待應用程序1完成更新操作,還要等應用程序1釋放占據鎖定,然後才能開始應用程序2的更新操作。可是,當我們聲明一個游標時不指定FOR UPDATE子句,應用程序1就會獲得並占據一個S(共享)鎖定,對該行加鎖(第1步),這意味著第二個應用程序用不著等待也可以獲得並占據一個S鎖(第2步)。然後,第一個應用程序試圖獲得一個U(更新)鎖以處理更新語句,但是它必須等第二個應用程序釋放其正占據著的S鎖(第3步)。同時,第二個應用程序也試圖獲得一個U鎖,並且由於第一個應用程序占據著S鎖而進入等待狀態(第4步)。這種情形就是死鎖,造成的後果是應用程序1或應用程序2的交易不得不回滾。

如果在DECLARE CURSOR語句中指定FOR UPDATE子句,當應用程序1提取該行時,將會給該行加上U鎖,應用程序2將會等待應用程序1釋放U鎖,這樣,兩個應用程序之間就不會發生死鎖了。

下面舉例說明如何在SELECT語句中使用FOR UPDATE子句。

  EXECSQLDECLAREc1CURSORFORSELECT*FROMemployee
FORUPDATEOFjob;
EXECSQLOPENc1;
EXECSQLFETCHc1INTO…;
If(strcmp(change,“YES”)==0)
EXECSQLUPDATEemployeeSETjob=:newjob
WHERECURRENTOFc1;
EXECSQLCLOSEc1;

對於CLI編程,我們可以使用函數SQLSetConnectAttr()將DB2 CLI的連接屬性SQL_ATTR_Access_MODE的值設置為:SQL_MODE_READ_WRITE,效果是一樣的。

4、指定OPTIMIZE FOR n ROWS子句

當我們希望提取的行數遠遠小於可能返回的行數時,在SELECT語句中指定OPTIMIZE FOR n ROWS子句。基於提取n行的假設,OP

TIMIZE FOR子句會影響查詢的優化,同時也決定了通訊緩沖區中行的數目。 

SELECTprojno,projname,repempFROMproject
WHEREdeptno=’D11’OPTIMIZEFOR10ROWS

運用行的分塊技術,通過在單一操作中一次性地提取一組行來減輕管理器的負擔,這些行存儲在緩沖區中,應用程序中的每一個FETCH請求都會從該緩沖區中提取下一行,如果指定OPTIMIZE FOR 10 ROWS,那麼,系統會以10行為一組返回給用戶。數據庫

需要注意的是,OPTIMIZE FOR n ROWS子句既不會限制可以提取的行數,也不會影響提取的結果,但是,該子句會影響應用程序的性能,如果最終提取的行數小於或等於n,該子句會改善性能;否則,如果大於n,性能就會下降。

5、指定FETCH FIRST n ROWS ONLY子句

如果不希望應用程序提取n行以上的記錄,我們可以在編程時指定FETCH FIRST n ROWS ONLY子句;反之,如果不指定該子句,結果集中可能就會有很多行(大於n)。注意,該子句不能與FOR UPDATE子句同時使用。

參看下面的例子,程序最多能提取5行。

SELECTprojno,projname,repempFROMproject
WHEREdeptno=’D11’
FETCHFIRST5ROWSONLY

該子句同OPTIMIZE FOR n ROWS子句一樣,也決定通訊緩沖區中行的數目;如果同時指定FETCH FIRST n1 ROWS ONLY子句和OPTIMIZE FOR n2 ROWS子句,則取n1和n2二者中的較小值作為通訊緩沖區的大小。

6、指定FOR FETCH ONLY子句

如果不想更新那些由SELECT語句提取的行,我們可以在SELECT語句中指定FOR FETCH ONLY子句,這麼做的好處是,處理應用程序提出的查詢請求時可以充分利用行的分塊技術,進而改善性能;該子句還能改善數據的並發性,因為使用該子句查詢的那些行上不再有獨占的鎖了。除了FOR FETCH ONLY子句,我們還可以使用FOR READ ONLY子句,二者是同義詞,功效一致。

7、避免數據類型轉換

我們應盡可能地避免數據類型的轉換,特別是數字的數據類型之間的轉換。當比較兩個值時,使用具有相同數據類型的項目進行比較效率會更高。例如,有兩張表:A和B,使用A的A1列和B的B1列進行兩表間的連接操作,如下所示:

SELECT*FROMA,B
WHEREA1=B1

如果列A1和B1的數據類型一致,則無需進行數據類型轉換;如果不一致,那麼,應用程序就會在運行時進行數據類型的轉換以比較二者的數值大小,從而影響應用程序的性能。例如,如果A1列的數據類型是decimal,而B1是integer,並且都有一個數值“123”,那麼這時就需要進行數據類型轉換了,因為表A將“123”存儲為“123C”(十六進制表示),而表B存儲為“7B”(十六進制表示)。

再者,進行數據類型轉換時,由於計算精度的限制,可能會導致錯誤的發生。

DB2 UDB提供許多數據類型,其中,有用於數字數據的SMALLINT,INTEGER,BIGINT,DECIMAL,REAL,DOUBLE;有用於字符數據的CHAR,VARCHAR,LONG VARCHAR,CLOB;也有用於雙字節字符數據的GRAPHIC,VARGRAPHIC,LONG VARGRAPHIC和DBCLOB,等等。由於數據庫的存儲容量和各種變量的處理成本都取決於數據類型,所以,我們在編程時如何選擇適當的數據類型就顯得十分重要,以下是選擇數據類型時應該遵循的一些准則:

● 對於比較短的列,盡量使用定長的CHAR而不是變長的VARCHAR。雖然,當數據的長度參差不齊時,VARCHAR可以節省數據庫存儲空間,但是系統需要花費額外的開銷去檢查每個數據的長度。

● 盡量使用VARCHAR或VARGRAPHIC而不是LONG VARCHAR或LONG VARGRAPHIC。VARCHAR列和LONG VARCHAR列的最大長度差不多,基本一致,VARCHAR列的最大長度是32672字節,LONG VARCHAR列的最大長度是32700字節;同樣地,VARGRAPHIC列和LONG VARGRAPHIC列的最大長度也相仿,VARGRAPHIC是16336字節,LONG VARGRAPHIC是16350字節。對於LONG VARCHAR列和LONG VARGRAPHIC列有一些限制,比如,這兩列中的數據不能存儲在數據庫緩沖池中。

● 如果不需要小數部分,則盡量使用整數(SMALLINT,INTEGER,BIGINT)而不是浮點數(REAL或DOUBLE)或十進制數(DECIMAL)。比較而言,整數的處理成本要廉價得多。

● 盡量使用日期--時間(DATE,TIME,TIMESTAMP)而不是字符(CHAR)。日期--時間數據類型會占用較少的數據庫存儲空間,而且,對於日期--時間數據類型的變量,我們還可以利用系統提供的一些內置函數(如YEAR,MONTH)對其進行計算、轉換等處理,一般來說,內置函數要比我們手工編寫的函數在性能、效率上都要高一些。

● 盡量使用數字的數據類型而不是字符的數據類型。

凡事都是一分為二,有利有弊的,我們不能既想節省數據庫的存儲空間,又希望降低應用程序的處理成本,二者只能取其一,不可兼得,這就要求我們在編程時不能僅局限於一點,應該通盤考慮整個應用程序的設計,綜合評估,權衡利弊,以求應用程序的性能更優。

結束語

一談起數據庫性能調整、優化,我們腦海中往往就會有這樣的概念:優化是系統維護時做的事情,屬於後期制作。其實不然,早在編程時就已經存在性能優化的問題了。本文就編寫SQL語句時應該注意的一些問題闡述一下筆者的觀點,不當之處還請大家批評指正。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved