本文詳細(xì)地講述了DB2數(shù)據(jù)庫(kù)中對(duì)INSERT語(yǔ)句性能優(yōu)化的過(guò)程。
1、INSERT的執(zhí)行步驟。
這些步驟中的每一步都有優(yōu)化的潛力,對(duì)此我們?cè)诤竺鏁?huì)一一討論。
(1)在客戶機(jī)準(zhǔn)備語(yǔ)句。對(duì)于動(dòng)態(tài) SQL,在語(yǔ)句執(zhí)行前就要做這一步,此處的性能是很重要的;對(duì)于靜態(tài) SQL,這一步的性能實(shí)際上關(guān)系不大,因?yàn)檎Z(yǔ)句的準(zhǔn)備是事先完成的。
(2)在客戶機(jī),將要插入的行的各個(gè) 列值組裝起來(lái),發(fā)送到 DB2 服務(wù)器。
(3)DB2 服務(wù)器確定將這一行插入到哪一頁(yè)中。
(4)DB2 在 用于該頁(yè)的緩沖池中預(yù)留一個(gè)位置。如果 DB2 選定的是一個(gè)已有的頁(yè),那么就需要讀磁盤;如果使用一個(gè)新頁(yè),則要在表空間(如果是SMS,也就是系統(tǒng)管理存儲(chǔ)的表空間)中為該頁(yè)物理地分配空間。插入了新行的每一頁(yè)最后都要從緩沖池寫(xiě)入到磁盤。
(5)在目標(biāo)頁(yè)中對(duì)該行進(jìn)行格式化,并獲得該行上的一個(gè)X(exclusive,獨(dú)占的) 行鎖。
(6)將反映該 insert 的一條記錄寫(xiě)入到日志緩沖區(qū)中。
(7)最后提交包含該 insert 的事務(wù),如果這時(shí)日志緩沖區(qū)中的記錄還沒(méi)有被寫(xiě)入日志文件的話,則將這些記錄寫(xiě)到日志文件中。
此外,還可能發(fā)生很多類型的附加處理,這取決于數(shù)據(jù)庫(kù)配置,例如,索引或觸發(fā)器的存在。這種額外的處理對(duì)于性能來(lái)說(shuō)也是意義重大的,我們?cè)诤竺鏁?huì)討論到。
2、insert 的替代方案
在詳細(xì)討論 insert 的優(yōu)化之前,讓我們先考慮一下 insert 的兩種替代方案:load 和 import。import 實(shí)用程序?qū)嶋H上是 SQL INSERT 的一個(gè)前端,但它的某些功能對(duì)于您來(lái)說(shuō)也是有用的。load 也有一些有用的額外功能,但是我們使用 load 而不使用 insert 的主要原因是可以提高性能。
(1)load 直接格式化數(shù)據(jù)頁(yè),而避免了由于插入導(dǎo)致的對(duì)每一行進(jìn)行處理的大部分開(kāi)銷(例如,日志記錄在這里實(shí)際上是消除了)。而且,load 可以更好地利用多處理器機(jī)器上的并行性。在 V8 load 中有兩個(gè)新功能,它們對(duì)于 load 成為 insert 的替代方案有著特別的功效,這兩個(gè)功能是:從游標(biāo)裝載和從調(diào)用層接口(CLI)應(yīng)用程序裝載。
(2)從游標(biāo)裝載
這種方法可用于應(yīng)用程序的程序代碼(通過(guò) db2Load API),或用于 DB2 腳本。下面是后一種情況的一個(gè)例子:
以下是代碼片段:
declare staffcursor cursor forselect * from staff;
load from staffcursor of cursor insert into myschema.new_staff;
這兩行可以用下面一行替代:
以下是代碼片段:
insert into myschema.new_staff select * from staff
同等效的 INSERT ... SELECT 語(yǔ)句相比,從游標(biāo)裝載幾乎可以提高 20% 的性能。
(3)從 CLI 裝載
這種方法顯然只限于調(diào)用層接口(CLI)應(yīng)用程序,但是它非常快。這種技巧非常類似于數(shù)組插入,DB2 附帶了這樣的示例,使用 load 時(shí)的速度是使用經(jīng)過(guò)完全優(yōu)化的數(shù)組插入時(shí)的兩倍,幾乎要比未經(jīng)優(yōu)化的數(shù)組插入快 10 倍。
3、所有 insert 可以改進(jìn)的地方
讓我們看看插入處理的一些必要步驟,以及我們可以用來(lái)優(yōu)化這些步驟的技巧。
(1) 語(yǔ)句準(zhǔn)備
作為一條 SQL 語(yǔ)句,INSERT 語(yǔ)句在執(zhí)行之前必須由 DB2 進(jìn)行編譯。這一步驟可以自動(dòng)發(fā)生(例如在 CLP 中,或者在一次 CLI SQLExecDirect 調(diào)用中),也可以顯式地進(jìn)行(例如,通過(guò)一條 SQL Prepare、CLI SQLPrepare 或 JDBC prepareStatement 語(yǔ)句)。該編譯過(guò)程牽涉到授權(quán)檢查、優(yōu)化,以及將語(yǔ)句轉(zhuǎn)化為可執(zhí)行格式時(shí)所需的其他一些活動(dòng)。在編譯語(yǔ)句時(shí),語(yǔ)句的訪問(wèn)計(jì)劃被存儲(chǔ)在包緩存中。
如果重復(fù)地執(zhí)行相同的 INSERT 語(yǔ)句,則該語(yǔ)句的訪問(wèn)計(jì)劃(通常)會(huì)進(jìn)入到包緩存中,這樣就免除了編譯的開(kāi)銷。然而,如果 insert 語(yǔ)句對(duì)于每一行有不同的值,那么每一條語(yǔ)句都將被看成是惟一的,必須單獨(dú)地進(jìn)行編譯。因此,將像下面這樣的重復(fù)語(yǔ)句:
以下是代碼片段:
insert into mytable values (1, 'abc')
insert into mytable values (2, 'def')
等等,換成帶有參數(shù)標(biāo)記的語(yǔ)句,一次準(zhǔn)備,重復(fù)執(zhí)行,這樣做是十分可取的:
以下是代碼片段:
insert into mytable values (?, ?)
使用參數(shù)標(biāo)記可以讓一系列的 insert 的運(yùn)行速度提高數(shù)倍。(在靜態(tài) SQL 程序中使用主機(jī)變量也可以獲得類似的好處。)
(2)發(fā)送列值到服務(wù)器
可以歸為這一類的優(yōu)化技巧有好幾種。最重要的一種技巧是在每條 insert 語(yǔ)句中包括多行,這樣就可以避免對(duì)于每一行都進(jìn)行客戶機(jī)-服務(wù)器通信,同時(shí)也減少了 DB2 開(kāi)銷。可用于多行插入的技巧有:
在 VALUES 子句中包含多行的內(nèi)容。例如,下面的語(yǔ)句將插入三行:INSERT INTO mytable VALUES (1, 'abc'), (2, 'def'), (3, 'ghi')
在 CLI 中使用數(shù)組插入(array insert)。這需要準(zhǔn)備一條帶參數(shù)標(biāo)記的 INSERT 語(yǔ)句,定義一個(gè)用于存儲(chǔ)要插入的值的數(shù)組,將該數(shù)組綁定到參數(shù)標(biāo)記,以及對(duì)于每個(gè)數(shù)組中的一組內(nèi)容執(zhí)行一次 insert。而且,示例程序 sqllib/samples/cli/tbload.c 提供了數(shù)組插入的基本框架(但是執(zhí)行的是 CLI LOAD)。從不使用數(shù)組改為使用包含 100 行的數(shù)組,可以將時(shí)間縮短大約 2.5 倍。所以應(yīng)該盡可能地使用包含至少 100 行的數(shù)組。
在 JDBC 中使用批處理操作。這跟 CLI 中的數(shù)組插入一樣,基于相同的概念,但是實(shí)現(xiàn)細(xì)節(jié)有所不同。當(dāng)通過(guò) prepareStatement 方法準(zhǔn)備了 insert 語(yǔ)句之后,剩下的步驟是針對(duì)每一列調(diào)用適當(dāng)?shù)?setXXXX 方法(例如,setString 或 setInt),然后是 addBatch。對(duì)于要插入的每一行,都要重復(fù)這些步驟,然后調(diào)用 executeBatch 來(lái)執(zhí)行插入。要查看這方面的例子,請(qǐng)參閱“參考資料”一節(jié)中的 JDBC Tutorial。
使用 load 將數(shù)據(jù)快速地裝入到一個(gè) staging 表中,然后使用 INSERT ... SELECT 填充主表。(通過(guò)這種方法節(jié)省下來(lái)的代價(jià)源于 load 的速度非??欤偌由?INSERT ... SELECT 是在 DB2 內(nèi)(在服務(wù)器上)傳輸數(shù)據(jù)的,從而消除了通信上的代價(jià)。一般情況下我們不會(huì)使用這種方法,除非在 INSERT ... SELECT 中還要另外做 load 無(wú)法完成的處理。
如果不可能在一條 insert 語(yǔ)句中傳遞多行,那么最好是將多條 insert 語(yǔ)句組成一組,將它們一起從客戶機(jī)傳遞到服務(wù)器。(不過(guò),這意味著每條 insert 都包含不同的值,都需要準(zhǔn)備,因而其性能實(shí)際上要比使用參數(shù)標(biāo)記情況下的性能更差一些。)將多條語(yǔ)句組合成一條語(yǔ)句可以通過(guò) Compound SQL 來(lái)實(shí)現(xiàn):
在 SQL 中,復(fù)合語(yǔ)句是通過(guò) BEGIN ATOMIC 或 BEGIN COMPOUND 語(yǔ)句創(chuàng)建的。
新聞熱點(diǎn)
疑難解答