欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
在 DB2 Universal Database 中提高 INSERT 性能的技巧
本文精妙地解釋了執行 insert 操作時(shí)所發(fā)生的事情,考察了 insert 的一些替代方案,并研究了影響 insert 性能的一些問(wèn)題,例如鎖、索引維護以及約束管理。

簡(jiǎn)介

在使用 DB2® Universal Database (UDB) 的時(shí)候,行的插入是我們要執行的最常見(jiàn)、也是最重要的任務(wù)之一。本文是關(guān)于優(yōu)化 insert 、尤其是插入量比較大的 insert 的一個(gè)技巧匯編。談到性能,往往都存在著(zhù)某些權衡,這里也不例外。我們將討論在優(yōu)化 insert 的過(guò)程中可能帶來(lái)的權衡問(wèn)題。例如,您對 insert 采用了某種優(yōu)化技巧,但是這種技巧可能要求在 insert 之后還要進(jìn)行附加的處理,或者可能影響查詢(xún)的性能。我會(huì )提供一些性能測試的結果,以便讓您了解很多優(yōu)化技巧的作用。 附錄 A包含了對這些結果的一個(gè)小結,并且編了號。我將在全文各處以測試編號來(lái)引用結果。在后面的 結束語(yǔ)一節中,總結了大部分有益的技巧,而 附錄 B則列出了本文給出的所有建議。在本文中,我們無(wú)意研究關(guān)于如何實(shí)現這些技巧的細節,但是這方面的信息可以在 DB2 手冊中找到。請閱讀 參考資料一節,以了解更多細節。





回頁(yè)首


INSERT 處理過(guò)程概述

首先讓我們快速地看看插入一行時(shí)的處理步驟。這些步驟中的每一步都有優(yōu)化的潛力,對此我們在后面會(huì )一一討論。

  1. 在客戶(hù)機準備 語(yǔ)句。對于動(dòng)態(tài) SQL,在語(yǔ)句執行前就要做這一步,此處的性能是很重要的;對于靜態(tài) SQL,這一步的性能實(shí)際上關(guān)系不大,因為語(yǔ)句的準備是事先完成的。
  2. 在客戶(hù)機,將要插入的行的各個(gè) 列值組裝起來(lái),發(fā)送到 DB2 服務(wù)器。
  3. DB2 服務(wù)器確定將這一行插入到哪一頁(yè)中。
  4. DB2 在 用于該頁(yè)的緩沖池中預留一個(gè)位置。如果 DB2 選定的是一個(gè)已有的頁(yè),那么就需要讀磁盤(pán);如果使用一個(gè)新頁(yè),則要在表空間(如果是 SMS,也就是系統管理存儲的表空間)中為該頁(yè)物理地分配空間。插入了新行的每一頁(yè)最后都要從緩沖池寫(xiě)入到磁盤(pán)。
  5. 在目標頁(yè)中對該行進(jìn)行格式化,并獲得該行上的一個(gè) X(exclusive,獨占的) 行鎖。
  6. 將反映該 insert 的一條 記錄寫(xiě)入到日志緩沖區中。
  7. 最后 提交包含該 insert 的事務(wù),如果這時(shí)日志緩沖區中的記錄還沒(méi)有被寫(xiě)入日志文件的話(huà),則將這些記錄寫(xiě)到日志文件中。
此外,還可能發(fā)生很多類(lèi)型的附加處理,這取決于數據庫配置,例如,索引或觸發(fā)器的存在。這種額外的處理對于性能來(lái)說(shuō)也是意義重大的,我們在后面會(huì )討論到。

 





回頁(yè)首


insert 的替代方案

在詳細討論 insert 的優(yōu)化之前,讓我們先考慮一下 insert 的兩種替代方案:load 和 import。import 實(shí)用程序實(shí)際上是 SQL INSERT 的一個(gè)前端,但它的某些功能對于您來(lái)說(shuō)也是有用的。load 也有一些有用的額外功能,但是我們使用 load 而不使用 insert 的主要原因是可以提高性能。這里我們不會(huì )進(jìn)一步討論 import,不過(guò)讀者可以參閱后面的 參考資料一節,在那里可以找到指向 Data Movement Utilities Guide 的鏈接,該指南討論了 import 與 load 之間的不同之處。

load 直接格式化數據頁(yè),而避免了由于插入導致的對每一行進(jìn)行處理的大部分開(kāi)銷(xiāo)(例如,日志記錄在這里實(shí)際上是消除了)。而且,load 可以更好地利用多處理器機器上的并行性。在 V8 load 中有兩個(gè)新功能,它們對于 load 成為 insert 的替代方案有著(zhù)特別的功效,這兩個(gè)功能是:從游標裝載和從調用層接口(CLI)應用程序裝載。

從游標裝載


這種方法可用于應用程序的程序代碼(通過(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
附錄 A中的 test 6 和 9 表明,同等效的 INSERT ... SELECT 語(yǔ)句相比,從游標裝載幾乎可以提高 20% 的性能。

從 CLI 裝載


這種方法顯然只限于調用層接口(CLI)應用程序,但是它非???。這種技巧非常類(lèi)似于數組插入( 后面會(huì )討論),DB2 附帶了這樣的示例,請查看 sqllib/samples/cli/tbload.c。通過(guò)查看附錄 A 中的 test 79我們可以看到,使用 load 時(shí)的速度是使用經(jīng)過(guò)完全優(yōu)化的數組插入(test 71)時(shí)的兩倍,幾乎要比未經(jīng)優(yōu)化的數組插入(比如 test 69)快 10 倍。





回頁(yè)首


所有 insert 可以改進(jìn)的地方

讓我們看看插入處理的一些必要步驟,以及我們可以用來(lái)優(yōu)化這些步驟的技巧。

1. 語(yǔ)句準備


作為一條 SQL 語(yǔ)句,INSERT 語(yǔ)句在執行之前必須由 DB2 進(jìn)行編譯。這一步驟可以自動(dòng)發(fā)生(例如在 CLP 中,或者在一次 CLI SQLExecDirect 調用中),也可以顯式地進(jìn)行(例如,通過(guò)一條 SQL Prepare、CLI SQLPrepare 或 JDBC prepareStatement 語(yǔ)句)。該編譯過(guò)程牽涉到授權檢查、優(yōu)化,以及將語(yǔ)句轉化為可執行格式時(shí)所需的其他一些活動(dòng)。在編譯語(yǔ)句時(shí),語(yǔ)句的訪(fǎng)問(wèn)計劃被存儲在包緩存中。

如果重復地執行相同的 INSERT 語(yǔ)句,則該語(yǔ)句的訪(fǎng)問(wèn)計劃(通常)會(huì )進(jìn)入到包緩存中,這樣就免除了編譯的開(kāi)銷(xiāo)。然而,如果 insert 語(yǔ)句對于每一行有不同的值,那么每一條語(yǔ)句都將被看成是惟一的,必須單獨地進(jìn)行編譯。因此,將像下面這樣的重復語(yǔ)句:
insert into mytable values (1, 'abc')
insert into mytable values (2, 'def')

等等,
換成帶有參數標記的語(yǔ)句,一次準備,重復執行,這樣做是十分可取的:
insert into mytable values (?, ?)

test 1test 2相比, 61-6465-68相比,我們可以看到,使用參數標記可以讓一系列的 insert 的運行速度提高數倍。(在靜態(tài) SQL 程序中使用主機變量也可以獲得類(lèi)似的好處。)

2. 發(fā)送列值到服務(wù)器


可以歸為這一類(lèi)的優(yōu)化技巧有好幾種。最重要的一種技巧是在每條 insert 語(yǔ)句中包括多行,這樣就可以避免對于每一行都進(jìn)行客戶(hù)機-服務(wù)器通信,同時(shí)也減少了 DB2 開(kāi)銷(xiāo)??捎糜诙嘈胁迦氲募记捎校?ul>

  • 在 VALUES 子句中包含多行的內容。例如,下面的語(yǔ)句將插入三行:INSERT INTO mytable VALUES (1, 'abc'), (2, 'def'), (3, 'ghi')
  • 在 CLI 中使用數組插入(array insert)。這需要準備一條帶參數標記的 INSERT 語(yǔ)句,定義一個(gè)用于存儲要插入的值的數組,將該數組綁定到參數標記,以及對于每個(gè)數組中的一組內容執行一次 insert。請參閱 CLI Guide and Reference以了解詳細信息。而且,示例程序 sqllib/samples/cli/tbload.c 提供了數組插入的基本框架(但是執行的是 CLI LOAD)。通過(guò)比較 test 68(使用了 10x 行)、71、73、74 和 78 各自所耗的時(shí)間,我們可以看到,從不使用數組改為使用包含 100 行的數組,可以將時(shí)間縮短大約 2.5 倍。所以應該盡可能地使用包含至少 100 行的數組。
  • 在 JDBC 中使用批處理操作。這跟 CLI 中的數組插入一樣,基于相同的概念,但是實(shí)現細節有所不同。當通過(guò) prepareStatement 方法準備了 insert 語(yǔ)句之后,剩下的步驟是針對每一列調用適當的 setXXXX 方法(例如,setString 或 setInt),然后是 addBatch。對于要插入的每一行,都要重復這些步驟,然后調用 executeBatch 來(lái)執行插入。要查看這方面的例子,請參閱“參考資料”一節中的 JDBC Tutorial。
  • 使用 load 將數據快速地裝入到一個(gè) staging 表中,然后使用 INSERT ... SELECT 填充主表。(請參閱后面的“ 通過(guò) staging 表和其他方法增加并行性”小節。)通過(guò)這種方法節省下來(lái)的代價(jià)源于 load 的速度非???,再加上 INSERT ... SELECT 是在 DB2 內(在服務(wù)器上)傳輸數據的,從而消除了通信上的代價(jià)。一般情況下我們不會(huì )使用這種方法,除非在 INSERT ... SELECT 中還要另外做 load 無(wú)法完成的處理。
  •  

    如果不可能在一條 insert 語(yǔ)句中傳遞多行,那么最好是將多條 insert 語(yǔ)句組成一組,將它們一起從客戶(hù)機傳遞到服務(wù)器。(不過(guò),這意味著(zhù)每條 insert 都包含不同的值,都需要準備,因而其性能實(shí)際上要比使用參數標記情況下的性能更差一些,前面“語(yǔ)句準備”一節已對此作了討論。)將多條語(yǔ)句組合成一條語(yǔ)句可以通過(guò) Compound SQL 來(lái)實(shí)現:

    • 在 SQL 中,復合語(yǔ)句是通過(guò) BEGIN ATOMIC 或 BEGIN COMPOUND 語(yǔ)句創(chuàng )建的。
    • 在 CLI 中,復合語(yǔ)句可以通過(guò) SQLExecDirect 和 SQLExecute 調用來(lái)建立。請參閱 CLI Guide and Reference Volume 1 以了解詳細信息。對于 DB2 V8 FixPak 4,另一種生成復合語(yǔ)句的方法是在(對一條預處理語(yǔ)句)發(fā)出多個(gè) SQLExecute 調用之前設置語(yǔ)句屬性 SQL_ATTR_CHAINING_BEGIN,并在調用之后設置語(yǔ)句屬性 SQL_ATTR_CHAINING_END。

     

    下面是關(guān)于該話(huà)題的其他一些建議:

    • 如果可能的話(huà),讓客戶(hù)機與要存取的數據庫使用相同的代碼頁(yè),以避免在服務(wù)器上的轉換代價(jià)。數據庫的代碼頁(yè)可以通過(guò)運行“get db cfg for <database>”來(lái)確定。
    • 在某些情況下,CLI 會(huì )自動(dòng)執行數據類(lèi)型轉換,但是這樣同時(shí)也會(huì )帶來(lái)看不見(jiàn)的(小小的)性能損耗。因此,盡量使插入值直接處于與相應列對應的格式。
    • 將應用程序中與插入相關(guān)的設置開(kāi)銷(xiāo)最小化。例如,當在 CLI 中使用數組插入時(shí),對于整個(gè)一組插入,應該盡量保證對于每一列只執行一次 SQLBindParameter,而不是對每一組數組內容都執行一次。對于個(gè)體來(lái)說(shuō),這些調用的代價(jià)并不高,但是這些代價(jià)是累積的。

     

    3. 找到存儲行的地方


    DB2 使用三種算法中的一種來(lái)確定將行插入到哪里。(如果使用了多維群集(Multi-dimensional Clustering,MDC),則另當別論,我們在這里不予討論。)要了解關(guān)于插入算法方面的細節,請參閱 DB2 V8 Administration Guide: Performance中提到的相關(guān)內容。

    缺省模式是,DB2 搜索散布在表的各頁(yè)上的自由空間控制記錄(Free Space Control Records,FSCR),以找到有足夠自由空間存放新行的頁(yè)。顯然,如果每頁(yè)上的自由空間都比較少的話(huà),就要浪費很多的搜索時(shí)間。為了應付這一點(diǎn),DB2 提供了 DB2MAXFSCRSEARCH 注冊表變量,以便允許將搜索范圍限制為少于缺省的 5 頁(yè)。

    當表是通過(guò) ALTER TABLE 以 APPEND 模式放置時(shí),就要使用第二種算法。這樣就完全避免了 FSCR 搜索,因為只需簡(jiǎn)單地將行直接放到表的末尾。

    當表有群集索引(clustering index)時(shí),就要用到最后一種算法。在這種情況下,DB2 試圖將每一行插入到有相似鍵值的一頁(yè)中。如果那一頁(yè)沒(méi)有空間了,DB2 就會(huì )嘗試附近的頁(yè),如果附近的頁(yè)也沒(méi)有空間,DB2 就進(jìn)行 FSCR 搜索。

    如果只考慮插入時(shí)間的優(yōu)化,那么使用 APPEND 模式對于批量插入是最快的一種方法,但是這種方法的效果遠不如我們這里討論的很多其他方法那么成效顯著(zhù)。請參考 test 22 和 test 23。第二好的方法應該是采用缺省算法,但是,如果在最佳環(huán)境中,更改 DB2MAXFSCRSEARCH 的值影響很小,而在一個(gè) I/O 約束較少的環(huán)境中,這種更改所造成的影響就比較可觀(guān)了。

    如果有群集索引,則對 insert 的性能會(huì )有很大的負面影響(test 32 和 38 表明幾乎有 20% 的開(kāi)銷(xiāo)),這一點(diǎn)也不驚奇,因為使用群集索引的目的就是通過(guò)在插入時(shí)做額外的工作來(lái)提高查詢(xún)(即 select)性能的。如果的確需要群集索引,那么可以通過(guò)確保有足夠的自由空間來(lái)使其對插入的影響降至最?。菏褂?ALTER TABLE 增加 PCTFREE,然后使用 REORG 預留自由空間。不過(guò),如果允許太多自由空間的存在,則可能導致查詢(xún)時(shí)需要讀取額外的頁(yè),這反而大大違反了使用群集索引的本意。另一種選擇是,在批量插入之前先刪除群集索引,而后再重新創(chuàng )建群集索引,也許這是最優(yōu)的方法(創(chuàng )建群集索引的開(kāi)銷(xiāo)跟創(chuàng )建常規索引的開(kāi)銷(xiāo)差不多,都不是很大,只是在插入時(shí)有額外的開(kāi)銷(xiāo))。

    4. 緩沖池、I/O 和頁(yè)清除


    每一條 insert 在執行時(shí),都是先將新行存儲在一個(gè)頁(yè)中,并最終將那個(gè)頁(yè)寫(xiě)到磁盤(pán)上。一旦像前面討論的那樣指定了頁(yè),那么在將行添加到該頁(yè)之前,該頁(yè)必須已經(jīng)在緩沖池中。對于批量插入,大部分頁(yè)都是最新指派給表的,因此讓我們關(guān)注一下對新頁(yè)的處理。

    如果表在系統管理存儲的(System Managed Storage,SMS)表空間中,當需要新頁(yè)時(shí),缺省情況下是從文件系統中分別為每一頁(yè)分配空間。但是,如果對數據庫運行了 db2empfa 命令,那么每個(gè) SMS 表空間就會(huì )為新頁(yè)一次性分配一個(gè)區段。test 11 和 test 82 表明,與區段大小為缺省的 32 頁(yè)的情況相比,運行 db2empfa 命令可以使對 SMS 表空間的插入快到兩倍,因為在 test 82 中對于一個(gè)頁(yè)有 32 次分配,而 test 11 中是 32 個(gè)頁(yè)一次分配。test 11 和 test 83-85 表明,如果區段大小小于 32 頁(yè),則性能會(huì )逐步下降,因為要進(jìn)行額外的分配,但是讓區段大于 32 這樣的建議也未必有幫助。我們建議運行 db2empfa 命令,并使用 32 頁(yè)的區段。

    對于數據庫管理的存儲(Database Managed Storage,DMS)表空間,空間是在創(chuàng )建表空間時(shí)就預先分配的,但是頁(yè)的區段則是在插入處理過(guò)程中指派給表的。與 SMS 相比,DMS 對空間的預分配可以提高大約 20% 的性能 -- 請參考 test 11 和 test 81。test 81 使用 DMS 文件表空間,而如果使用了 DMS raw 表空間的話(huà),還可以多得到一點(diǎn)好處。使用 DMS 時(shí),更改區段大小并沒(méi)有明顯的效果。

    如果表上有索引,則對于每個(gè)插入的行,都要添加一個(gè)條目到每條索引。這要求在緩沖池中存在適當的索引頁(yè)。晚些時(shí)候我們將討論索引的維護,但是現在只需記住,插入時(shí)對緩沖池和 I/O 的考慮也類(lèi)似地適用于索引頁(yè),對于數據頁(yè)也是一樣。

    隨著(zhù)插入的進(jìn)行,越來(lái)越多的頁(yè)中將填入被插入的行,但是,DB2 不要求在 insert 或 Commit 后將任何新插入的或更新后的數據或索引寫(xiě)入到磁盤(pán)。(這是由于 DB2 的 writeahead 日志記錄算法。但是有一個(gè)例外,這將在關(guān)于日志記錄的小節中論述到。)然而,這些頁(yè)需要在某一時(shí)刻寫(xiě)到磁盤(pán)上,這個(gè)時(shí)刻可能會(huì )在數據庫關(guān)閉時(shí)才會(huì )輪到。

    一般來(lái)說(shuō),對于批量插入,您會(huì )希望積極地進(jìn)行 異步頁(yè)清除(asynchronous page cleaning),這樣在緩沖池中就總有可用于新頁(yè)的空余位置。頁(yè)清除率,或者說(shuō)總缺頁(yè)率,可能導致計時(shí)上的很大不同,使得性能比較容易產(chǎn)生誤解。例如,如果使用 100,000 頁(yè)的緩沖池,并且不存在頁(yè)清除,則批量插入在結束前不會(huì )有任何新的或更改過(guò)的(“臟的”)頁(yè)寫(xiě)到磁盤(pán)上,但是隨后的操作(例如選擇,甚至乎關(guān)閉數據庫)都將被大大推遲,因為這時(shí)有至多 100,000 個(gè)在插入時(shí)產(chǎn)生的臟頁(yè)要寫(xiě)到磁盤(pán)上。另一方面,如果在同一情況下進(jìn)行了積極的頁(yè)清除,則批量插入過(guò)程可能要花更長(cháng)的時(shí)間,但是此后緩沖池中的臟頁(yè)要少一些,從而使得隨后的任務(wù)執行起來(lái)性能更佳。至于那些結果中到底哪個(gè)要更好些,我們并不是總能分得清,但是通常來(lái)說(shuō),將所有臟頁(yè)都存儲在緩沖池中是不可能的,所以為了取得最佳性能,采取有效的頁(yè)清除是有必要的。

    為了盡可能好地進(jìn)行頁(yè)清除:

    • 將 CHNGPGS_THRESH 數據庫配置參數的值從缺省的 60 減少到 5 這么低。這個(gè)參數決定緩沖池中臟頁(yè)的閾值百分比,當臟頁(yè)達到這個(gè)百分比時(shí),就會(huì )啟動(dòng)頁(yè)清除。
    • 嘗試啟用注冊表變量 DB2_USE_ALTERNATE_PAGE_CLEANING(在 DB2 V8 FixPak 4 中最新提供)。通過(guò)將這個(gè)變量設置成 ON,可以為頁(yè)清除提供一種比缺省方法(基于 CHNGPGS_THRESH 和 LSN 間隙觸發(fā)器)更積極的方法。我沒(méi)有評測過(guò)其效果。請參閱 FixPak 4 Release Notes 以了解這方面的信息。
    • 確保 NUM_IOCLEANERS 數據庫配置參數的值至少等于數據庫中物理存儲設備的數量。

     

    至于 I/O 本身,當需要建立索引時(shí),可以通過(guò)使用盡可能大的緩沖池來(lái)將 I/O 活動(dòng)減至最少。(請參閱后面的“ 索引維護”一節。)如果不存在索引,則使用較大的緩沖池幫助不大,而只是推遲了 I/O。也就是說(shuō),它允許所有新頁(yè)暫時(shí)安放在緩沖池中,但是最終仍需要將這些頁(yè)寫(xiě)到磁盤(pán)上。

    當發(fā)生將頁(yè)寫(xiě)到磁盤(pán)的 I/O 時(shí),通過(guò)一些常規的 I/O 調優(yōu)步驟可以加快這一過(guò)程,例如:

    • 將表空間分布在多個(gè)容器(這些容器映射到不同磁盤(pán))。
    • 盡可能使用最快的硬件和存儲管理配置,這包括磁盤(pán)和通道速度、寫(xiě)緩存以及并行寫(xiě)等因素。
    • 避免 RAID5(除非是與像 Shark 這樣有效的存儲設備一起使用)。

     

    5. 鎖


    缺省情況下,每一個(gè)插入的行之上都有一個(gè) X 鎖,這個(gè)鎖是在該行創(chuàng )建時(shí)就開(kāi)始有的,一直到 insert 被提交。有兩個(gè)跟 insert 和鎖相關(guān)的性能問(wèn)題:

    • 為獲得和釋放鎖而產(chǎn)生的 CPU 開(kāi)銷(xiāo)。
    • 可能由于鎖沖突而導致的并發(fā)問(wèn)題。

     

    對于經(jīng)過(guò)良好優(yōu)化的批量插入,由獲得每一行之上的一個(gè) X 鎖以及后來(lái)釋放該鎖引起的 CPU 開(kāi)銷(xiāo)是比較可觀(guān)的。對于每個(gè)新行之上的鎖,惟一可以替代的是表鎖(DB2 中沒(méi)有頁(yè)鎖)。test 11 和 test 101 表明,當使用表鎖時(shí),耗時(shí)減少了 3%。有 3 種情況可以導致表鎖的使用,在討論表鎖的缺點(diǎn)之前,我們先用一點(diǎn)時(shí)間看看這 3 種情況:

    • 運行 ALTER TABLE <name> LOCKSIZE TABLE。這將導致 DB2 為隨后使用該表的所有 SQL 語(yǔ)句使用一個(gè)表鎖,直到 locksize 參數改回到 ROW。
    • 運行 LOCK TABLE <name> IN EXCLUSIVE MODE。這將導致表上立即上了一個(gè) X 鎖。注意,在下一次提交(或回滾)的時(shí)候,這個(gè)表將被釋放,因此,如果您要運行一個(gè)測試,測試中每 N 行提交一次,那么就需要在每次提交之后重復執行 LOCK TABLE。
    • 使用缺省鎖,但是讓 LOCKLIST 和 MAXLOCKS 數據庫配置參數的值比較小。當獲得少量的行鎖時(shí),行鎖就會(huì )自動(dòng)地逐漸升級為表鎖。

     

    當然,所有這些的缺點(diǎn)就在于并發(fā)的影響:如果表上有一個(gè) X 鎖,那么其他應用程序除非使用了隔離級別 UR(未提交的讀),否則都不能訪(fǎng)問(wèn)該表。如果知道獨占訪(fǎng)問(wèn)不會(huì )導致問(wèn)題,那么就應該盡量使用表鎖。但是,即使您堅持使用行鎖,也應記住,在批量插入期間,表中可能存在數千個(gè)有 X 鎖的新行,所以就可能與其他使用該表的應用程序產(chǎn)生沖突。通過(guò)一些方法可以將這些沖突減至最少:

    • 確保鎖的升級不會(huì )無(wú)故發(fā)生。您可能需要加大 LOCKLIST 和/或 MAXLOCKS 的值,以允許插入應用程序有足夠的鎖。
    • 對于其他的應用程序,使用隔離級別 UR。
    • 對于 V8 FixPak 4,或許也可以通過(guò) DB2_EVALUNCOMMITTED 注冊表變量來(lái)減少鎖沖突:如果將該變量設置為 YES,那么在很多情況下,只能獲得那些符合某個(gè)謂詞的行上的鎖,而并不是獲得被檢查的所有行上的鎖。
    • 發(fā)出一個(gè) COMMIT 命令以釋放鎖,因此如果更頻繁地提交的話(huà)就足以減輕鎖沖突的負擔。

     

    注意

    • 在 V7 中,存在涉及 insert 和鍵鎖的并發(fā)問(wèn)題,但是在 V8 中,由于提供了 type-2 索引,這些問(wèn)題實(shí)際上已經(jīng)不見(jiàn)了。如果要遷移到 V8 中來(lái),那么應該確保使用帶 CONVERT 關(guān)鍵字的 REORG INDEXES 命令,以便將索引從 type-1 轉換為 type-2。
    • 在 V7 中,插入過(guò)程中可能使用 W 或 NW 鎖,但是在 V8 中只有在使用了 type-1 索引或者隔離級別為 RR 的情況下才會(huì )出現這兩種鎖。因此,應盡可能避免這兩種情況。
    • 一條 insert 所據有的鎖(通常是一個(gè) X 鎖)通常不會(huì )受隔離級別的影響。例如,使用隔離級別 UR 不會(huì )阻止從插入的行上獲得鎖。然而,如果使用了 INSERT ... SELECT,則隔離級別將影響從 SELECT 獲得的鎖。

     

    6. 日志記錄


    缺省情況下,每條 insert 都會(huì )被記錄下來(lái),以用于恢復。日志記錄首先被寫(xiě)到內存中的日志緩沖池,然后再寫(xiě)到日志文件,通常是在日志緩沖池已滿(mǎn)或者發(fā)生了一次提交時(shí)寫(xiě)到日志文件的。對批量插入的日志記錄的優(yōu)化實(shí)際上就是最小化日志記錄寫(xiě)的次數,以及使寫(xiě)的速度盡可能快。

    這里首先考慮的是日志緩沖池的大小,這由數據庫配置參數 LOGBUFSZ 來(lái)控制。該參數缺省值為 8 頁(yè)或 32 K,這與大多數批量插入所需的理想日志緩沖池大小相比要小些。舉個(gè)例子,對于一個(gè)批量插入,假設對于每一行的日志內容有 200 字節,則在插入了 160 行之后,日志緩沖池就將被填滿(mǎn)。如果要插入 1000 行,因為日志緩沖池將被填滿(mǎn)幾次,再加上提交,所以大概有 6 次日志寫(xiě)。如果將 LOGBUFSZ 的值增加到 64 頁(yè)(256K)或者更大,緩沖池就不會(huì )被填滿(mǎn),這樣的話(huà)對于該批量插入就只有一次日志寫(xiě)(在提交時(shí))。test 104 和 test 105 表明,通過(guò)使用更大的 LOGBUFSZ 可以獲得大約 13% 的性能提升。較大日志緩沖池的不利之處是,緊急事故恢復所花的時(shí)間可能要稍微長(cháng)一點(diǎn)。

    減少日志寫(xiě)的另一種可能性是對新行要插入到的那個(gè)表使用“ALTER TABLE <name> ACTIVATE NOT LOGGED INITIALLY”(NLI)。如果這樣做了,那么在該工作單元內不會(huì )記錄任何 insert 操作,但是這里存在兩個(gè)與 NLI 有關(guān)的重要問(wèn)題:

    • 如果有一條語(yǔ)句失敗,那么這個(gè)表將被標記為不可訪(fǎng)問(wèn)的,并且需要被刪除掉。這與其他恢復問(wèn)題(請參閱 SQL Reference 關(guān)于 Create Table 的討論)一起使得 NLI 在很多情況下不能成為可行的方法。
    • 在工作單元最后進(jìn)行的提交,必須等到在此工作單元內涉及的所有臟頁(yè)都被寫(xiě)到磁盤(pán)之后才能完成。這意味著(zhù)這種提交要占用大量的時(shí)間。實(shí)際上, test 6 和 7已表明,如果沒(méi)有積極地進(jìn)行頁(yè)清除,那么在使用 NLI 的情況下,Insert 加上提交所耗費的總時(shí)間要更長(cháng)一些。不過(guò),test 8 表明,將 NLI 與積極的頁(yè)清除一起使用的時(shí)候,可以大大減少耗時(shí)。如果使用 NLI,就要瞪大眼睛盯緊提交操作所耗費的時(shí)間。

     

    至于提高日志寫(xiě)的速度,有下面一些可能性:

    • 將日志與新行所要插入到的表分別放在不同的磁盤(pán)上。
    • 在操作系統層將日志分放到多個(gè)磁盤(pán)。
    • 考慮為日志使用原始設備(raw device),但是要注意,這樣管理起來(lái)要更困難些。
    • 避免使用 RAID 5,因為它不適合于寫(xiě)密集型(write-intensive)活動(dòng)。

     

    7. 提交


    提交迫使將日志記錄寫(xiě)到磁盤(pán)上,以保證提交的插入肯定會(huì )存在于數據庫中,并且釋放新行上的鎖。這些都是有價(jià)值的活動(dòng),但是因為 Commit 總是要牽涉到同步 I/O(對于日志),而 insert 則不會(huì ),所以 Commit 的開(kāi)銷(xiāo)很容易高于 insert 的開(kāi)銷(xiāo)。因此,在進(jìn)行批量插入時(shí),每一行都提交一次的做法對于性能來(lái)說(shuō)是很糟糕的,所以應確保不使用自動(dòng)提交(對于 CLI 和 CLP 來(lái)說(shuō)缺省情況正是如此)。建議大約每 1000 行提交一次:test 61-78 表明,當每 1000 行而不是一兩行提交一次時(shí),性能可以提高大概 10 倍。不過(guò),一次提交多于 1000 行只能節省少量的時(shí)間,但是一旦出現失敗,恢復起來(lái)所花的時(shí)間要更多。

    對上述方法的一種修正:如果 MINCOMMIT 數據庫配置參數的值大于 1 (缺省值),則 DB2 就不必對每次 commit 都進(jìn)行一次同步 I/O,而是等待,并試圖與一組事件一起共享日志 I/O。對于某些環(huán)境來(lái)講,這樣做是有好處,但是對于批量插入常常沒(méi)有作用,甚至有負作用,因此,如果要執行的關(guān)鍵任務(wù)是批量插入,就應該讓 MINCOMMIT 的值保持為 1。





    回頁(yè)首


    可以選擇性地進(jìn)行改進(jìn)的地方

    對于一次 insert,有幾種類(lèi)型的處理將自動(dòng)發(fā)生。如果您的主要目標只是減少插入時(shí)間,那么最簡(jiǎn)單的方法是避免所有這些處理的開(kāi)銷(xiāo),但是如果從總體上考慮的話(huà),這樣做未必值得。讓我們依次進(jìn)行討論。

    索引維護


    對于插入的每一行,必須添加一個(gè)條目到表上的每個(gè)索引中(包括任何主鍵索引)。這一過(guò)程主要有兩方面的代價(jià):

    • 遍歷每個(gè)索引樹(shù),在樹(shù)的每一層搜索一個(gè)頁(yè),以確定新條目必須存儲在哪里(索引條目總是按鍵順序存儲的),這一過(guò)程所引起的 CPU 開(kāi)銷(xiāo);
    • 將所有搜索到的頁(yè)讀入緩沖池,并最終將每個(gè)更新后的頁(yè)寫(xiě)到磁盤(pán)上的 I/O 開(kāi)銷(xiāo)。

     

    更壞的場(chǎng)景是,在索引維護期間有大量的隨機 I/O。假設要插入 10,000 行,在索引的緩沖池中有 5000 頁(yè),并且要插入的各行的鍵值隨機分布在整個(gè)鍵范圍內。那么,有 10,000 個(gè)這么多的葉子頁(yè)(可能還有些非葉子頁(yè))需要進(jìn)入緩沖池,以便對它們進(jìn)行搜索和/或更新,對于一個(gè)給定的葉子頁(yè),它預先已經(jīng)在緩沖池中的概率只有 10%。對于每次的 insert,需要讀磁盤(pán)的概率如此之高,使得這種場(chǎng)景往往性能很差。

    對于逐行插入,將新行添加到已有的索引中比起創(chuàng )建一個(gè)新索引來(lái)代價(jià)要高得多。如果是插入到一個(gè)空表,應該總是在進(jìn)行了列插入之后創(chuàng )建索引。(注意,如果使用了 load,則應該 預先創(chuàng )建索引。)如果要插入到一個(gè)已經(jīng)填充過(guò)的表,那么在列插入之前刪除索引,并在列插入之后重新創(chuàng )建索引,這種方法可能是最快的,但是只有在要插入相當多的行 -- 大概大于表的 10-20% 的時(shí)候,才能這么說(shuō)。如果為索引表空間使用較大的緩沖池,并且盡可能地將不同 insert 排序,以便鍵值是排好序的,而不是隨機的,就可以幫助加快索引維護。

    附錄 A 中的 test 31-37給出的結果表明,insert 的耗時(shí)是如何隨著(zhù)索引的數目以及創(chuàng )建索引的時(shí)機而變化的??偠灾?,與沒(méi)有索引相比,有 4 條索引的情況下 insert 可能要多耗費數倍的時(shí)間,而如果在 insert 之后才創(chuàng )建索引,就可以將總耗時(shí)(insert 耗時(shí)加上創(chuàng )建索引的耗時(shí))縮短 1/4 到幾乎 1/2。

    如果關(guān)鍵目標是將 insert 的性能最優(yōu)化,那么增加索引的 PCTFREE 時(shí),就可能減少在隨機插入索引條目時(shí)出現頁(yè)拆分(page split)的次數。所以這樣做時(shí)要小心,不過(guò),太多的自由空間意味著(zhù)大量的索引頁(yè),這對查詢(xún)的性能乃至 insert 處理本身都會(huì )產(chǎn)生負面影響。

    約束驗證


    這一類(lèi)的開(kāi)銷(xiāo)包括檢查約束驗證和外鍵約束(參照完整性(RI))驗證。檢查約束開(kāi)銷(xiāo)很低(請參閱附錄 A 中的 test 11-13),這很大程度上是因為不需要對每一行使用 I/O(因為要檢查的值是在行內,只是要進(jìn)行一些計算而已)。

    如果有外鍵,則插入一行就是另外一回事了。對于每個(gè)外鍵,都必須在父表中進(jìn)行一次查找,以確保有父行存在。雖然這種查找是通過(guò)主鍵索引進(jìn)行的,但是這仍然要占用 CPU 循環(huán)來(lái)進(jìn)行搜索,而且可能還要占用 I/O 將索引頁(yè)讀入緩沖池。test 11、14 和 15 表明,當有兩個(gè)外鍵時(shí),insert 的耗時(shí)要翻一番。

    在填充表之后使用 CREATE INDEX 比起通過(guò)一條一條的 INSERT 語(yǔ)句建立索引來(lái)代價(jià)要小些,同樣,使用 ALTER TABLE 創(chuàng )建外鍵(即進(jìn)行驗證)作為批量操作,比起在每次 insert 期間的驗證所增加的代價(jià)的總和來(lái),要小一些。比較 test 14 和 test 16、test 15 和 test 17,我們可以看到,在 insert 之后創(chuàng )建外鍵可以將總耗時(shí)減少大約 40%。

    如果可能的話(huà),在大量插入行到一個(gè)表之前,應該先使用 ALTER TABLE 刪除表上的所有約束,在插入之后再重新創(chuàng )建這些約束(仍是使用 ALTER TABLE),只有在插入的行不到該表中所有行的 10-20% 時(shí)才可不必這樣做。

    有些應用程序本身也會(huì )做一些檢查,以確保表之間的關(guān)系是有效的。也就是說(shuō),在插入一個(gè)子行之前,應用程序會(huì )讀一個(gè)父行,以確保父行存在。如果這種檢查得以正確執行,那么在數據庫中定義外鍵約束將增加額外的開(kāi)銷(xiāo)。但是,至少有三個(gè)原因可以說(shuō)明為什么在數據庫中定義這些約束要更好些:

    1. 沒(méi)有檢查的應用程序會(huì )更簡(jiǎn)單。
    2. 如果由 DB2 來(lái)負責檢查,性能要稍微好一些。
    3. 在數據庫中定義約束使 DB2 可以知道表之間的關(guān)系,并且在某些情況下允許 DB2 根據這樣的知識選擇更好的訪(fǎng)問(wèn)計劃。

     

    如果應用程序本身的檢查不能少,那么最好的替代方案是在 DB2 中定義外鍵,但是在 CREATE TABLE 或 ALTER TABLE 中帶上 NOT ENFORCED 子句,這樣就避免了檢查開(kāi)銷(xiāo),而優(yōu)化器又能使用關(guān)系知識。

    觸發(fā)器執行


    如果在一個(gè)表上定義了一個(gè)或多個(gè)進(jìn)行 Insert 操作的觸發(fā)器,那么每次的 insert 都將引起觸發(fā)器定義中的動(dòng)作的執行。由于那些被觸發(fā)的動(dòng)作通常是一條或多條 INSERT、UPDATE 或 DELETE 語(yǔ)句,因此,在數據量很大的 insert 中,觸發(fā)器的開(kāi)銷(xiāo)會(huì )很大。附錄 A 中的 test 18-21表明,增加觸發(fā)器會(huì )導致 insert 的性能減慢數倍。索引和約束可以臨時(shí)刪除,但是應用程序知識卻必須知道何時(shí)避免觸發(fā)器的執行是可接受的。也就是說(shuō),知道何時(shí)不會(huì )導致數據完整性問(wèn)題。如果避免觸發(fā)器的執行是可接受的,您可以通過(guò) 參考資料中列出的文章里所描述的技術(shù)來(lái)臨時(shí)禁用觸發(fā)器。

    標識列和序列對象


    這兩種方法可以讓 DB2 自動(dòng)生成整型列值,這通常是在 insert 期間進(jìn)行的。應該清楚的主要性能問(wèn)題是,由于可恢復性的原因,生成的值必須做日志記錄。為了減少日志記錄的開(kāi)銷(xiāo),可以將這些值預存(緩存)起來(lái),每當緩存用完時(shí),才寫(xiě)一條日志記錄。缺省情況下是緩存 20 個(gè)值。

    附錄 A 中 test 41-51的結果表明,如果沒(méi)有緩沖的話(huà),耗時(shí)會(huì )非常大(幾乎要比缺省情況慢 9 倍),而如果使用比缺省情況更大的緩存,則可以縮減大半的時(shí)間,并且將使用 Identity(標識)或 Sequence(序列) 的時(shí)間減至不到原先的 20%。如果您要在使用 Identity 還是 Sequence 之間作選擇的話(huà),那么我告訴您使用 Identity 要好出幾個(gè)百分點(diǎn)。

    生成的列


    當插入行到一個(gè)表,并且該表用“generated as”子句定義了一個(gè)或多個(gè)列,例如: CREATE TABLE t1 (c1 CHAR(5), c2 CHAR(5) generated always as (UPPER(c1))) ,這時(shí),為建立生成的值而進(jìn)行的函數調用將導致附加的開(kāi)銷(xiāo)。然而,這種開(kāi)銷(xiāo)非常小,不至于影響您使用這種功能的決心。

    “refresh immediate”物化查詢(xún)表(MQT)的重新生成


    MQT 可用于通過(guò)預先計算聚合值來(lái)增強查詢(xún)性能。例如:
    create table staffsum as
    ( select count(salary) as salcount, sum(salary) as salsum, dept from staff group by dept )
    data initially deferred refresh immediate

    如果 MQT 被定義為“refresh immediate”,則在每次 insert 時(shí)將重新計算 MQT 中的聚合,因此通常來(lái)講,對帶有 refresh immediate MQT 的表執行列插入不大可取。不過(guò),DB2 會(huì )盡其所能優(yōu)化重新計算,例如掃描總結表,而不是整個(gè)基本表。我們建議對于涉及 MQT 的 insert 運行 Explain,以便清楚幕后情況。





    回頁(yè)首


    其他方面的考慮

    分區表(在 DPF 中,以前的 V7 EEE)


    當插入一行到一個(gè)分區表(使用 DB2 V8 的 Data Partitioning Feature (DPF))時(shí),首先要散列(hash)該行的分區鍵值以確定必須將該行插入到哪個(gè)分區,然后 DB2 將該行發(fā)送到那個(gè)分區。這種一次一行的處理方式比較慢,前面我們已看到,在 CLI 中,這種方式與數組插入方式比起來(lái)的確很慢,而且因為還需要將每一行從協(xié)調分區發(fā)送到目標分區,使得情況更加糟糕。

    為了把開(kāi)銷(xiāo)降至最低,可以使用緩沖插入(buffered insert)。如果使用了這種方法,定向于某個(gè)給定分區的散列行首先會(huì )進(jìn)入一個(gè)緩沖區中,然后成組地發(fā)送到那個(gè)分區,而不是一次發(fā)送一行。您可以通過(guò) Prep 或 Bind 命令的“INSERT BUF”選項來(lái)引起緩沖插入。要了解關(guān)于緩沖插入的細節,請參閱 Application Development Guide 的參考資料:Programming Client Applications。在開(kāi)發(fā)者園地也有關(guān)于基于 Java(只包括 SQLJ)的緩沖插入的文章,請參閱后面的“參考資料”。

    在 DPF 環(huán)境中,如果對于重復的批量插入要求絕對最大的性能,那么您可能需要研究?jì)蓚€(gè)相關(guān)的 API。第一個(gè) API 是 sqlugtpi,它讓?xiě)贸绦蚩梢垣@得一個(gè)表的分區信息。之后就可以使用這種信息,再結合 sqlugrpn API 來(lái)找到一行所屬的分區號。您可以使用這些 API 將屬于某個(gè)給定分區的所有數據組到一起,然后連接到那個(gè)分區,這樣就不需要在分區之間傳輸數據,對于每個(gè)分區都重復這么做。這種方法可以取得非??斓男阅?,但是要花一定的精力來(lái)確保這種方法在有多個(gè)數據類(lèi)型、代碼頁(yè)等等的情況下也能十分有效。

    DPF 插入可能引起的另一個(gè)問(wèn)題是,要插入到的那個(gè)表(子表)上可能有外鍵約束。假設父表和子表有不同的分區鍵。那么每個(gè)子行的父親一般會(huì )在一個(gè)不同的分區上,因此,對于大多數插入的行,對其父親的驗證檢查就需要從子分區跨越到父分區。對此的解決辦法是,讓父表和子表的分區鍵相同(這對于查詢(xún)性能來(lái)說(shuō)不是最好的選擇),或者,如果使用了多個(gè)邏輯分區,就可以將 DB2 注冊表變量設置為 YES。

    通過(guò) Staging 表以及其他方法增加并行性


    通過(guò)使用 staging 表可以為某些場(chǎng)景下的 insert 提高性能。通常的用法是,不是批量插入行到一個(gè)表中,而是使用 LOAD 命令將行裝載到一個(gè) staging 表中;然后,就可以使用 INSERT ... SELECT 將行插入到主表。不管 LOAD 還是 INSERT ... SELECT,都比常規的插入要快得多,即使將這兩步加起來(lái)也常常要比常規插入快些。不過(guò),單單就性能而言,在 V8 中還是使用 load 直接將表裝載到主表要快些,因為在 V8 中的 load 不像 V7 中那樣有并發(fā)限制。

    除了數據的消息傳遞以外,致使您在 V8 中仍想使用 staging 表的主要原因是,這樣可以將批量插入拆散成能夠并行運行的更小的塊。在一個(gè)有多個(gè)處理器的系統上,每條插入將在一個(gè) DB2 代理中運行,并且不會(huì )占用多于一個(gè)的處理器,即使將 DBMINTRA_PARALLEL 參數被設為 ON 也是如此。例如,如果要在一臺 8-way 的機器上插入 1M 的行,一般的插入過(guò)程通常對 CPU 的利用不會(huì )多于 12% (100 / 8)。(另一方面,load 則會(huì )自動(dòng)使用百分比大得多的 CPU,這也是它比 insert 更可取的另一個(gè)原因。)相反,您可以將 1M 的行裝載到一個(gè) staging 表中,然后運行 8 條并發(fā)的 insert ... Select 語(yǔ)句,8 條 Select 語(yǔ)句中都有謂詞,每條語(yǔ)句從 staging 表中檢索大約 1/8 的惟一的行子集。

    最后,您可以通過(guò)一個(gè)多線(xiàn)程應用程序運行并發(fā)的插入,其中每個(gè)線(xiàn)程做它自己的插入。

    對于一條 INSERT ... SELECT,只要能使選擇更快,就可以減少整條語(yǔ)句的耗時(shí),但是這超出了本文的范圍,不適合進(jìn)行詳細的討論。下面列出了一些可能性。注意,這些只適用于選擇部分。而同樣的這些因素對于插入部分一般沒(méi)什么幫助。

    • 添加索引(不是在插入表上的索引?。?。
    • 使用大的緩沖池。
    • 使用并行(INTRA_PARALLEL=YES 且 DFT_DEGREE > 1)。
    • 使用隔離級別 UR (例如用一個(gè) WITH UR 子句)。

     

    插入 LOB 和 LONG 列


    這些類(lèi)型的列是惟一的,它們不會(huì )緩存在緩沖池中。因此,任何包括一個(gè)或多個(gè)這種列的 insert 都會(huì )使得這些列被直接寫(xiě)到磁盤(pán)上,用 DB2 術(shù)語(yǔ)來(lái)說(shuō)就是“直接寫(xiě)(direct write”)。您可以想像,這會(huì )使 LOB/LONG 的 insert 比“一般” 的 insert 要慢得多: test 91中使用了一個(gè) CLOB 列,這種情況比基線(xiàn)測試(test 11,有一個(gè) CHAR 列)要慢 9 倍以上。有這樣一些優(yōu)化的可能性:

    • 將 LOB 或 LONG 改為 VARCHAR。這允許發(fā)生緩沖池的緩存。這可能要求將表放入到一個(gè)頁(yè)寬較大(例如 32 K)的表空間中,因為頁(yè)寬必須大到足以裝下所有的非 LOB 和非 LONG 列。
    • 使用 SMS 或 DMS 文件表空間,這樣便允許操作系統的緩存抵消某些性能上的降低。
    • 為 LOB/LONG 使用最佳的存儲/硬件配置。
    • 嘗試為 LOB 列使用 COMPACT 和 NOT LOGGED 屬性。這兩個(gè)屬性對于我這個(gè)小測試來(lái)說(shuō)沒(méi)有多大提高,但是當使用了大量數據的時(shí)候,效果就出來(lái)了。

     

    優(yōu)化級別


    對于沒(méi)有約束的簡(jiǎn)單插入,將優(yōu)化級別從缺省值(5)改為 1 的測試雖然將優(yōu)化器的算法變得更廉價(jià),但是并沒(méi)有對性能產(chǎn)生很大的變化。如果要經(jīng)常準備插入語(yǔ)句,插入牽涉到約束,或者有選擇部分,那么使用較低的優(yōu)化級別可能會(huì )有好處。相反,如果插入部分很小,而選擇部分比較復雜,那么將優(yōu)化級別從 5 增至更大將帶來(lái)好處。

    利用源表(source table)插入/更新目標表(MERGE 語(yǔ)句)


    一個(gè)相當常見(jiàn)的數據庫任務(wù)就是利用一個(gè)源表更新一個(gè)目標表。舉個(gè)特定的例子,比如取源表中的每一行,如果該行不在目標表中,那么就將該行插入到目標表,否則就更新目標行。在 V8 中可以使用 MERGE 語(yǔ)句獨立完成上述任務(wù),而不必多次執行不同的語(yǔ)句,從而性能也就更好一些。





    回頁(yè)首


    監視和調優(yōu) insert

    當您試圖監視和調優(yōu) insert 時(shí),基本任務(wù)跟大多數其他的性能分析沒(méi)什么不同:找出瓶頸所在,然后直接處理瓶頸。欲確定瓶頸,首先就是利用操作系統工具查看 CPU、I/O、內存和網(wǎng)絡(luò )消耗。這樣應該就可以讓您排除某些方面,而將注意力放在一兩個(gè)方面。對操作系統實(shí)用程序的深入討論超出了本文的范圍。

    盡量不要被非必要的問(wèn)題轉移了視線(xiàn)。例如,如果 CPU 利用率是 100%,那么這時(shí)減少 I/O 很可能無(wú)法提高性能,而當以后 CPU 瓶頸已經(jīng)解除時(shí),這樣的更改本來(lái)可能非常有用,但是您可能會(huì )因為上一次的失敗而不再作這樣的更改了。

    應該盡量讓?xiě)贸绦蛟诓僮髌陂g的不同時(shí)刻報告插入的速率。例如,如果知道在運行后的第 10 分鐘與第 5 分鐘時(shí)各自的每秒插入次數是否相同,是很有用的。通常,當 insert 開(kāi)始的時(shí)候,有一小段較慢的啟動(dòng)時(shí)間,然后當緩沖池填充了內容并且沒(méi)有數據頁(yè)的 I/O 時(shí),就有一段速度比較快的時(shí)期。接著(zhù),當開(kāi)始將數據頁(yè)往外寫(xiě)的時(shí)候,速率又會(huì )慢下來(lái),如果頁(yè)清除或者 I/O 子系統不是最優(yōu)的,則更是如此。

    對于非常大的批量插入,通常在某一時(shí)刻插入的速率會(huì )趨于平穩。如果不是這樣,那么通常是因為要在插入期間創(chuàng )建索引,使得需要創(chuàng )建越來(lái)越多的索引頁(yè),并且可能還要進(jìn)行隨機的 I/O 操作,讀取已有的索引頁(yè)以便更新它們。如果的確是上述情況,那么使用更大的緩沖池是最好的解決辦法,但是為索引頁(yè)增加更多的自由空間也有所幫助。

    現在讓我們看看可以幫助您監視和調優(yōu) insert 的關(guān)鍵 DB2 實(shí)用程序:Snapshots(快照監視)、Event Monitoring(事件監視)和 Explain。要了解關(guān)于快照監視和事件監視的更多信息,請參閱 System Monitor Guide and Reference;至于 Explain,請參閱 Administration Guide: Performance。

    快照監視


    快照監視可以提供大量信息片斷來(lái)描述在插入的處理期間發(fā)生了什么事情。您可以使用以下步驟獲得所有可以得到的信息(或者也可以選擇獲得信息的子集):

    • 使用 UPDATE MONITOR SWITCHES 命令打開(kāi)所有開(kāi)關(guān)。
    • 運行 RESET MONITOR ALL重設計數器。這樣更易于在一次測試的過(guò)程中比較不同快照并找出不同。
    • 等一段標準長(cháng)度的時(shí)間,例如 1 分鐘或者 5 分鐘,然后發(fā)出 GET SNAPSHOT FOR ALL ON <database>。 重復前兩步,以獲得多個(gè)用于比較的快照。

     

    大多數與 insert 相關(guān)的信息都可以在數據庫快照中找到,并且大部分的這些信息也都會(huì )在適當的緩沖池、應用程序和表空間快照中以更大的粒度提供。

    以下是數據庫快照中最相關(guān)的幾行:

    Buffer pool data writes                    = 500            Asynchronous pool data page writes         = 500            Buffer pool index writes                   = 0            Asynchronous pool index page writes        = 0            Total buffer pool write time (ms)          = 25000            Total elapsed asynchronous write time      = 25000            LSN Gap cleaner triggers                   = 21            Dirty page steal cleaner triggers          = 0            Dirty page threshold cleaner triggers      = 0            Update/Insert/Delete statements executed   = 100000            Rows inserted                              = 100000            

    請注意連續快照中的“Rows inserted”,看看在批量插入期間插入的速率是否有變化。大多數其他的值都反映了 I/O 量和頁(yè)清除的效力。至于后者,理想情況下您可以看到,所有數據寫(xiě)都是同步的,而所有緩沖池寫(xiě)時(shí)間都是異步的(就像上面輸出的那樣);如果不是這樣,嘗試降低 CHNGPGS_THRESH 和/或增加 NUM_IOCLEANERS。

    通常在動(dòng)態(tài) SQL 快照中可以找到關(guān)于 insert 的附加信息??纯纯偤臅r(shí),并將其與用戶(hù)和系統 CPU 時(shí)間相比較,這樣做是十分有用的。耗時(shí)與 CPU 之間的差值大部分在于 I/O 部分,所以,哪個(gè)地方占去了大部分的時(shí)間以及哪個(gè)地方需要調優(yōu)也就很清楚了。

    以下是一個(gè) 100,000 行的 CLI 數組插入的動(dòng)態(tài) SQL 快照條目的一個(gè)子集。注意,盡管應用程序只發(fā)送那個(gè)數量的 1/10 那么多的數組,“Number of executions”仍是對于每一行都有一個(gè)。

    Number of executions               = 100000            Number of compilations             = 1            Rows written                       = 100000            Buffer pool data logical reads     = 102120            Total execution time (sec.ms)      = 13.830543            Total user cpu time (sec.ms)       = 10.290000            Total system cpu time (sec.ms)     = 0.130000            Statement text                     = INSERT into test1 values(?, ?, ?, ?, ?)            

    快照輸出中的其他信息:

    • “Lock waits”和“Time database waited on locks” -- 使用它們來(lái)查看插入的行上的鎖是否引起其他應用程序的并發(fā)問(wèn)題。
    • Table Snapshot --“Rows Written”將反映插入(或更新)的行的數目。

     

    事件監視


    當事件在服務(wù)器上發(fā)生時(shí),通過(guò) DB2 事件監視器可以獲得關(guān)于事件的性能信息。為了分析 insert 的性能,需要為語(yǔ)句創(chuàng )建一個(gè)事件監視器,并在 insert 執行期間激活該事件監視器。雖然有點(diǎn)過(guò)分,但是事件監視器的輸出會(huì )顯示每條 insert 語(yǔ)句的耗時(shí)。對于 OLTP 型的應用程序,對語(yǔ)句運行事件監視的開(kāi)銷(xiāo)相當高,其輸出也十分冗長(cháng),所以應注意不要讓事件監視運行太長(cháng)的時(shí)間。即使是幾秒鐘也會(huì )產(chǎn)生數兆的輸出。您可以將監視器信息寫(xiě)入到一個(gè)表中,以便于對結果的分析,例如性能趨勢。

    下面是一個(gè)濃縮的示例語(yǔ)句事件,在一系列的 10,000 個(gè) CLI 數組插入中每 10 行對應一次這樣的語(yǔ)句事件。每個(gè)數組只有一個(gè)事件,在這里就是每 10 行有一個(gè)事件。

    17) Statement Event ...            Appl Handle: 9            Appl Id: *LOCAL.wilkins.0953D9033443            -------------------------------------------            Type     : Dynamic            Operation: Execute            Section  : 4            Creator  : NULLID            Package  : SYSSH200            Text     : INSERT into test1 values(?, ?)            -------------------------------------------            Start Time: 01-28-2004 22:34:43.918444            Stop Time:  01-28-2004 22:34:43.919763            Exec Time:  0.001319 seconds            Number of Agents created: 1            User CPU: 0.000000 seconds            System CPU: 0.000000 seconds            Fetch Count: 0            Rows read: 0            Rows written: 10            Internal rows deleted: 0            Internal rows updated: 0            Internal rows inserted: 0            Bufferpool data logical reads: 10            SQLCA:            sqlcode: 0            sqlstate: 00000            

    Explain


    如果 insert 的性能不像預期的那么好,則有可能是因為有“隱藏”的處理發(fā)生。前面已經(jīng)討論過(guò),這種處理可能以不同的形式出現,例如索引維護、約束驗證或者觸發(fā)器執行。對 insert 運行某種形式的 Explain (例如 Visual Explain,或者 Explain 語(yǔ)句加上 db2exfmt),就可以揭示大多數額外的處理(除了索引維護)。如果額外的處理可能是性能問(wèn)題的起因,那么可以消除這種額外的處理。

    作為一個(gè)簡(jiǎn)單的例子,下面的圖(由 db2exfmt 產(chǎn)生)展示了“Insert into test1 values (?, ?, ...)”語(yǔ)句的訪(fǎng)問(wèn)計劃。您可以猜出這里的額外處理是什么嗎?回答就在下面。

                                   Rows            RETURN            (   1)            Cost            I/O            |            0.333333            TBSCAN            (   2)            28.2956            1            /----+---\            0.04            1            FILTER   TABFNC: SYSIBM            (   3)        GENROW            28.2266            1            |            1            NLJOIN            (   4)            28.1268            1            /---------+--------\            1                       1            INSERT                  IXSCAN            (   5)                  (   7)            25.5248                 2.60196            1                       0            /---+--\\                   |            1          116               120            TBSCAN  TABLE: WILKINS    INDEX: SYSIBM            (   6)       test1       SQL0401290925513            0.0048            0            |            1            TABFNC: SYSIBM            GENROW            

    在我給出答案之前:Explain 對于 INSERT ... SELECT 語(yǔ)句也十分有用。insert 本身是非??斓?,但是 SELECT 可能存在一個(gè)訪(fǎng)問(wèn)計劃問(wèn)題,這個(gè)問(wèn)題會(huì )拖慢整個(gè)語(yǔ)句。通過(guò) Explain 就可以揭示這一切。
    答案:
    上述訪(fǎng)問(wèn)計劃是針對將行插入到擁有外鍵關(guān)系的子表的 insert 的。其中有一個(gè) insert(步驟 5)與索引掃描(步驟 7)之間的嵌套循環(huán)連接(NLJOIN,步驟 4 )。索引掃描實(shí)際上是對父表進(jìn)行主鍵查找,并完成外鍵約束驗證。在這種情況下,來(lái)自額外處理的開(kāi)銷(xiāo)相對來(lái)說(shuō)就比較?。核饕龗呙璧某杀竟烙嬛挥?2.60196 timerons(timerons 是(主要)結合了 CPU 和 I/O 代價(jià)的成本單位),而 insert 本身的成本是 25.5248 timerons。例如,如果有一個(gè)觸發(fā)器的話(huà),那么就會(huì )在訪(fǎng)問(wèn)計劃中的一個(gè)或多個(gè) insert、Update 或 Delete 條目中反映出來(lái)。

    順便提一下,當您在訪(fǎng)問(wèn)計劃中看到“GENROW”時(shí),其實(shí)就是一個(gè)“generate row”步驟。這代表用于后續步驟的臨時(shí)行的創(chuàng )建,這里不需要擔心。





    回頁(yè)首


    結束語(yǔ)

    在本文中,我們給出了多種提高 insert 性能的方法。請參閱 附錄 B以了解這些方法的完整清單。下面是最重要的一部分方法,每種方法在某些情況下可以使性能快上兩倍:

    • 盡可能使用 Load。
    • 使用參數標記,以避免對于每一行都有 Prepare 成本。
    • 每 N 行發(fā)出一次 Commit,其中 N 是一個(gè)比較大的數,例如 1000。千萬(wàn)不要每一行都提交,因此要小心自動(dòng)提交情況。
    • 一次插入一組行。
    • 將 insert 期間出現約束、索引和觸發(fā)器的機會(huì )降至最少。
    • 如果使用 SMS 表空間,則運行 db2empfa。
    • 優(yōu)化“special features”的使用:帶分區表的緩沖插入,用于 Identity 和 Sequence 值的大的緩存。

     

    我們希望本文可以讓您很好地了解在 DB2 insert 處理期間所發(fā)生的事情,以及如何監視和提高其性能。





    回頁(yè)首


    附錄 A -- 性能評測

    該附錄舉例說(shuō)明了在本文中討論的那些優(yōu)化技術(shù)可以帶來(lái)的一些好處(以及對非優(yōu)化方法的沖擊)。每次測試都至少運行 2 次,并且結果一致,但是該結果跟您在自己環(huán)境中看到的結果不一定完全吻合。特別地,這些結果是在一個(gè) I/O 強度比理想情況下更大的一個(gè)系統上得到的,因為用戶(hù)表空間和日志被放在相同的文件系統中,并且是在相同的兩個(gè)磁盤(pán)上。因此,能減少 CPU 開(kāi)銷(xiāo)的改進(jìn)策略通常還有比這里提到的更多的好處。

    下面幾條適用于所有這些測試,如有例外則會(huì )另外注明:

    • 小型 RS/6000 系統上的 DB2 V8 FixPak 4。
    • 使用了本地客戶(hù)機。
    • 為用戶(hù)數據使用了 SMS 表空間,并對數據庫運行了 db2empfa 命令以建立多頁(yè)文件分配。表空間頁(yè)寬/區段大小/預取大小采用缺省值(4K,32,32)。 要插入到的那個(gè)表一開(kāi)始為空,并且沒(méi)有索引。
    • 除了 test 1-2、8、61-68 和 79 以外,所有測試都是在 CLI 中使用數組插入來(lái)完成的。
    • 每 1000 行有一次 Commit。

     

    表 1. 準備語(yǔ)句和使用數組的效果


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    1 CLI -- 對每一行執行 SQLExecDirect 10,000 180.63
    2 CLI -- 預處理語(yǔ)句,但是每次只插入 1 行 10,000 92.05
    3 CLI -- 預處理語(yǔ)句,并且是數組插入(每次插入 10 行) 10,000 12.85

    test 1-3 中自動(dòng)提交是處于啟用狀態(tài)的,因而對每一次 insert 都有一次 Commit。也就是說(shuō),在 test 1 和 test 2 中,對于每一行都有一次 Commit,在 test 3 中對于每 10 行有一次 Commit。至于 Commit 的性能影響,請參考 test 61-78。

    表 2. 用一個(gè)表的內容填充另一個(gè)表


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    6 帶日志記錄的 INSERT ... SELECT 100,000 15.66
    7 帶 NOT LOGGED INITIALLY 選項的 INSERT ... SELECT 100,000 16.43
    8 與 test 7 一樣,但是 CHNGPGS_THRESH = 5 100,000 11.80
    9 從游標裝載(與 test 6-8 相同的 Select) 100,000 12.95

    test 6-9 計算了利用一個(gè)表填充另一個(gè)表的時(shí)間,包括 Commit 的時(shí)間。test 7 表明,使用 NOT LOGGED INITIALLY (NLI) 實(shí)際上會(huì )導致性能下降,因為需要在 Commit 時(shí)將新頁(yè)寫(xiě)到磁盤(pán)上:Commit 占了大半的時(shí)間。然而,當 test 8 中采用了更積極的頁(yè)清除時(shí),性能就大大提高了,這主要是因為 Commit 的時(shí)間縮短了超過(guò) 5 秒鐘。test 9 表明,通過(guò)使用 Load 而不是 insert ... Select,性能提高了 17%,并且沒(méi)有 test 6 中那樣的 NLI 風(fēng)險。

    表 3. 檢查約束、外鍵和觸發(fā)器的影響


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    11 CLI -- 與 test 3 相同,但是有 100K 的行,提交 1000 次 100,000 31.51
    12 CLI -- 與 test 11 相同,但是有 1 個(gè)檢查約束 100,000 33.65
    13 CLI -- 與 test 11 相同,但是有 2 個(gè)檢查約束 100,000 36.63
    14 CLI -- 與 test 11 相同,但是有 1 個(gè)外鍵(FK) 100,000 55.37
    15 CLI -- 與 test 11 相同,但是有 2 個(gè)外鍵(FK) 100,000 72.71
    16 CLI -- 與 test 14 相同,但是 Insert 之后添加了 FK 100,000 32.84
    17 CLI -- 與 test 15 相同,但是 Insert 之后添加了 FK 100,000 38.89
    18 CLI -- 與 test 11 相同,但是有 1 個(gè) 進(jìn)行 Insert 操作的觸發(fā)器 100,000 67.57
    19 CLI -- 與 test 11 相同,但是有 2 個(gè) 進(jìn)行 Insert 操作的觸發(fā)器 100,000 175.95
    20 CLI -- 與 test 11 相同,但是有 1 個(gè) 進(jìn)行 Update 操作的觸發(fā)器 100,000 54.71
    21 CLI -- 與 test 11 相同,但是有 2 個(gè) 進(jìn)行 Update 操作的觸發(fā)器 100,000 150.18
    22 CLI -- 與 test 11 相同,但是有 1M 的行 100,000 282.02
    23 CLI -- 與 test 22 相同,但是 APPEND 的狀態(tài)是 ON 100,000 281.64

    顯然,檢查約束的使用對性能有一點(diǎn)小小的影響,但是外鍵和觸發(fā)器的影響卻非常大。在 test 18 和 test 19 中,各自的觸發(fā)器都將一行插入到基本表所在的相同表空間內的一個(gè)不同的表中,這暴露了數據庫和日志磁盤(pán)上的 I/O 瓶頸。在 test 20 和 test 21 中,各自的觸發(fā)器在一個(gè)只有一行的表中增加了一個(gè)“insert count”列;雖然這里不像 test 18 和 test 19 那樣有其他的行,但是更新開(kāi)銷(xiāo)和日志記錄仍導致 insert 運行起來(lái)比沒(méi)有觸發(fā)器的情況下慢很多。雖然通過(guò)標準數據庫 I/O 調優(yōu)可以改進(jìn) test 18-21,但關(guān)鍵是在大規模插入期間應盡可能避免存在約束。

    表 4. 在插入前后創(chuàng )建索引的影響


    test # 插入方法/注解 # 行數 索引個(gè)數 耗時(shí)(秒)
    31 CLI -- 與 test 3 相同,但是有 100K 行,提交 1000 次 100,000 0 31.51
    32 CLI -- 與 test 31 相同,但是有 1 個(gè)索引 100,000 1 53.73
    33 CLI -- 與 test 31 相同,但是有 2 個(gè)索引 100,000 2 83.26
    34 CLI -- 與 test 31 相同,但是有 3 個(gè)索引 100,000 3 108.21
    35 CLI -- 與 test 31 相同,但是有 4 個(gè)索引 100,000 4 141.63
    36 CLI -- 與 test 35 相同,但是在插入之后創(chuàng )建索引 100,000 4 (*) 73.75
    37 CLI -- 與 test 32 相同,但是在插入之后創(chuàng )建索引 100,000 1 39.44
    38 CLI -- 與 test 32 相同,但是索引被群集起來(lái) 100,000 1 62.93

    在 test 32-35 中,索引是在插入之前創(chuàng )建的,而在 test 36-37 中索引是在插入之后創(chuàng )建的(并且,對于后一次測試,耗時(shí)包括 insert 的時(shí)間加上 CREATE INDEX 語(yǔ)句的時(shí)間)。

    表 5. 使用標識符或序列


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    41 CLI -- 與 test 31 相同(沒(méi)有 Identity 或 Sequence) 100,000 31.51
    42 CLI -- 與 test 41 相同,但是有 Identity 列,無(wú)緩存 100,000 896.61
    43 CLI -- 與 test 41 相同,但是有 Identity 列,緩存 5 100,000 212.52
    44 CLI -- 與 test 41 相同,但是有 Identity 列,緩存 20(缺?。?/td> 100,000 99.06
    45 CLI -- 與 test 41 相同,但是有 Identity 列,緩存 100 100,000 61.62
    46 CLI -- 與 test 41 相同,但是有 Identity 列,緩存 1000 100,000 37.51
    47 CLI -- 與 test 41 相同,但是有 Sequence,無(wú)緩存 100,000 896.92
    48 CLI -- 與 test 41 相同,但是有 Sequence,緩存 5 100,000 212.58
    49 CLI -- 與 test 41 相同,但是有 Sequence,緩存 20(缺?。?/td> 100,000 101.87
    50 CLI -- 與 test 41 相同,但是有 Sequence,緩存 100 100,000 66.55
    51 CLI -- 與 test 41 相同,但是有 Sequence,緩存 1000 100,000 39.55

    test 41-51 表明,如果緩存很小或者沒(méi)有緩存的話(huà),使用 Identity 或 Sequence 對性能有很大的負面影響,但是通過(guò)使用較大的緩存可以使這種開(kāi)銷(xiāo)幾乎可以忽略不計。

    表 6. 展示使用大型數組和每次提交很多 insert 的效果


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    1 CLI -- 對每一行執行 SQLExecDirect(同上) 10,000 183.55
    61 CLI -- 與 test 1 相同,每 5 行一次 Commit 10,000 118.41
    62 CLI -- 與 test 1 相同,每 10 行一次 Commit 10,000 114.23
    63 CLI -- 與 test 1 相同,每 100 行一次 Commit 10,000 103.43
    64 CLI -- 與 test 1 相同,每 1000 行一次 Commit 10,000 102.86
    2 CLI -- 預處理語(yǔ)句,但是每次插入 1 行(同上) 10,000 92.05
    65 CLI -- 與 test 2 相同,每 5 行一次 Commit 10,000 22.34
    66 CLI -- 與 test 2 相同,每 10 行一次 Commit 10,000 20.78
    67 CLI -- 與 test 2 相同,每 100 行一次 Commit 10,000 10.11
    68 CLI -- 與 test 2 相同,每 1000 行一次 Commit 10,000 7.58
    69 CLI -- 與 test 3 相同(10x 行);數組大小為 10,每 10 行一次 Commit 10,000 118.18
    70 CLI -- 與 test 69 相同,但是數組大小為 10,每 100 行一次 Commit 10,000 56.71
    71 CLI -- 與 test 69 相同,但是數組大小為 10,每 1000 行一次 Commit 10,000 30.09
    72 CLI -- 與 test 69 相同,但是數組大小為 100,每 100 行一次 Commit 10,000 50.65
    73 CLI -- 與 test 69 相同,但是數組大小為 100,每 1000 行一次 Commit 10,000 24.27
    74 CLI -- 與 test 69 相同,但是數組大小為 1000,每 1000 行一次 Commit 10,000 23.43
    75 CLI -- 與 test 69 相同,但是數組大小為 2,每 2 行一次 Commit 10,000 471.82
    76 CLI -- 與 test 69 相同,但是數組大小為 2,每 10 行一次 Commit 10,000 155.21
    77 CLI -- 與 test 69 相同,但是數組大小為 2,每 100 行一次 Commit 10,000 74.82
    78 CLI -- 與 test 69 相同,但是數組大小為 2,每 1000 行一次 Commit 10,000 48.51
    79 CLI -- 通過(guò) SQLSetStmtAttr(數組大小為 10)使用 Load 10,000 13.68

    前面的測試展示了使用大型數組和每次提交大量的行所帶來(lái)的好處,其中后者尤為重要。

    表 7. 使用 SMS 或 DMS


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    11 CLI -- SMS,運行了 db2empfa(同上) 100,000 31.51
    81 與 test 11 相同,但是使用 DMS 文件表空間 100,000 25.52
    82 與 test 11 相同,但是 db2empfa 沒(méi)有運行(SMS) 100,000 62.87
    83 與 test 11 相同,但是 extentsize = 4 (并且 prefetchsize = 4) 100,000 38.37
    83 與 test 11 相同,但是 extentsize = 4 (并且 prefetchsize = 4) 100,000 38.37
    84 與 test 11 相同,但是 extentsize = 8 (并且 prefetchsize = 8) 100,000 34.61
    85 與 test 11 相同,但是 extentsize = 16 (并且 prefetchsize = 16) 100,000 31.75

    前面的測試表明,對于 SMS,運行 db2empfa 是取得良好的 insert 性能的關(guān)鍵,而使用小于 32 頁(yè)的區段是有害的。DMS 比 SMS 更好一些。

    表 8. 使用 CLOB 列的效果


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    11 CLI -- (同上) 100,000 31.51
    91 與 test 11 相同,但是 CHAR(10) 列現在是 CLOB(10) 100,000 286.49

    前面的比較展示了使用 CLOB 列(雖然非常短)帶來(lái)的巨大影響。我們還對 test 91 的變種做過(guò)嘗試,但是結果仍然十分接近,對于 CLOB 列不管選擇作不作日志記錄,或者是否進(jìn)行壓縮,都影響不大。

    表 9. 使用表鎖而不是使用行鎖


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    11 CLI -- (同上) 100,000 31.51
    101 與 test 11 相同,但是使用了 LOCKSIZE TABLE 100,000 30.58

    上述比較表明,使用表鎖而不是行鎖可以節省大約 3% 的耗時(shí)。

    表 10. 改變 LOGBUFSZ


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    104 與 test 71 相同,但是每次提交 10K 的行(LOGBUFSZ=8) 100,000 28.53
    105 與 test 104 相同,但是 LOGBUFSZ = 256 100,000 24.91

    test 105 表明,如果將 LOGBUFSZ 升至足夠高,以免在 Insert 期間因為日志緩沖區被填滿(mǎn)而被迫將日志寫(xiě)到磁盤(pán),這樣做可以提高大約 13% 的性能。

    表 11. 使用多種優(yōu)化


    test # 插入方法/注解 # 行數 耗時(shí)(秒)
    22 CLI -- (同上) 1,000,000 282.02
    111 與 test 22 相同,但是使用了所有優(yōu)化(見(jiàn)下面) 1,000,000 160.45
    1(*) CLI -- (與 test 1 相同,但是增加到了 1,000,000 行) 1,000,000 ~18000.00

    test 111 使用了前面試過(guò)的所有優(yōu)化技術(shù)來(lái)替代基線(xiàn):使用 DMS 而不是 SMS,使用 1000 行的數組(而不是 10 行),每 10000 行提交一次(而不是 1000 行),使用 LOCKSIZE TABLE,使 APPEND 的狀態(tài)為 ON,并且 LOGBUFSZ 設為 256。注意,與最初的基線(xiàn)(test 1)相比,test 22 已經(jīng)做了一些優(yōu)化。這里將 test 1 擴展到了 1,000,000 行,以表明僅僅更改少量因素就可能造成性能上的巨大差異。





    回頁(yè)首


    附錄 B -- 對于批量插入的優(yōu)化建議清單

    下面是在本文前面詳細討論過(guò)的一些建議。請記住,這些建議的影響程度千差萬(wàn)別,有些建議對于除 Insert 之外的其他任務(wù)還可能有負面影響。

    1. 只準備 Insert 語(yǔ)句一次,在語(yǔ)句中使用參數標記,然后多次執行該語(yǔ)句。如果經(jīng)常要準備語(yǔ)句,那么可以試著(zhù)調低優(yōu)化級別。
    2. 通過(guò) CLI 數組或 JDBC 批處理操作,在每次 Insert 中包括多行,并盡可能地將處理工作從數組/批處理循環(huán)中移出來(lái),以?xún)?yōu)化應用程序。
    3. 將多條 Insert 組到一起(Compound SQL)。
    4. 讓客戶(hù)機應用程序和數據庫使用相同的代碼頁(yè)。
    5. 避免客戶(hù)機與數據庫之間的數據類(lèi)型轉換。
    6. 避免使用 LOB 和 LONG 列;如果無(wú)法避免的話(huà),請參考優(yōu)化對這兩種列的使用的建議。
    7. 為表使用 APPEND 模式,或者將 DB2MAXFSCRSEARCH (注冊表)設置成一個(gè)較低的值。
    8. 盡可能避免表上有任何索引,尤其是不要有群集索引。
    9. 在數據頁(yè)上預留適當大小的自由空間(如果使用了 APPEND 模式,則預留的空間為 0)。
    10. 在索引頁(yè)上預留適當大小的自由空間(如果是隨機 insert,則預留的自由空間應大于 10%)。
    11. DMS 表空間是最好的,但是如果使用了 SMS,那么運行 db2empfa 并使用 32 頁(yè)或更大的區段。
    12. 使用大的緩沖池,如果必須在 Insert 期間建立索引的話(huà),更應如此。
    13. 通過(guò)降低 CHNGPGS_THRESH 和增加 NUM_IOCLEANERS (DB CFG),確保有效的頁(yè)清除。在 V8 FP4 中,要考慮 DB2_USE_ALTERNATE_PAGE_CLEANING(注冊表)。
    14. 將數據、索引和日志散布在多個(gè)磁盤(pán)上,日志使用的磁盤(pán)應不同于其他東西使用的磁盤(pán)。對于數據、索引和日志,應避免 RAID 5。
    15. 使用 LOCK TABLE 或 ALTER TABLE 建立表一級的鎖。如果存在并發(fā)問(wèn)題,那么可能需要增加 LOCKLIST 和/或 MAXLOCKS,以確保不會(huì )出現獨占鎖升級的現象。
    16. 考慮 DB2_EVALUNCOMMITTED (注冊表,V8 FP4),以減少鎖對其他應用程序的影響。同時(shí)還為其他應用程序使用隔離級別 UR,以減少鎖對它們的影響。
    17. 增加 LOGBUFSZ(DB CFG)。
    18. 為 Insert 表使用 ACTIVATE NOT LOGGED INITIALLY,但是要清楚恢復問(wèn)題。
    19. 每過(guò) N 行之后再 Commit,其中 N 是一個(gè)較大的數,比如 1000。
    20. 盡量減少表上的觸發(fā)器、檢查和外鍵約束以及生成的列。
    21. 盡可能避免 IDENTITY 和 SEQUENCE,如果不能避免,則使用較大的緩存。
    22. 避免將行插入到帶有“refresh immediate”MQT 的表中。
    23. 在 DPF 環(huán)境中,使用緩沖插入并避免分區間的傳輸。
    24. 使用 Load 和 staging 表或多線(xiàn)程應用程序,以實(shí)現 Insert 的并行性。
    25. 對于 Insert ... Select,確保對 Select 部分進(jìn)行優(yōu)化。
    26. 使用 Explain 來(lái)發(fā)現“隱藏”的處理。
    27. 并且使用盡可能快的 CPU 和磁盤(pán),這一點(diǎn)是不用說(shuō)的。
    本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
    打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
    猜你喜歡
    類(lèi)似文章
    mysql 觸發(fā)器
    觸發(fā)器(Trigger)(三)
    mysql-操作觸發(fā)器
    SQL詳解
    轉貼:SQL SERVER面試題1
    新手MySQL工程師必備命令速查手冊
    更多類(lèi)似文章 >>
    生活服務(wù)
    分享 收藏 導長(cháng)圖 關(guān)注 下載文章
    綁定賬號成功
    后續可登錄賬號暢享VIP特權!
    如果VIP功能使用有故障,
    可點(diǎn)擊這里聯(lián)系客服!

    聯(lián)系客服

    欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久