寫(xiě)出高性能SQL語(yǔ)句的十三條法則 軟件測試
1、首先要搞明白什么叫執行計劃?
執行計劃是數據庫根據SQL語(yǔ)句和相關(guān)表的統計信息作出的一個(gè)查詢(xún)方案,這個(gè)方案是由查詢(xún)優(yōu)化器自動(dòng)分析產(chǎn)生的,比如一條SQL語(yǔ)句如果用來(lái)從一個(gè)10萬(wàn)條記錄的表中查1條記錄,那查詢(xún)優(yōu)化器會(huì )選擇“索引查找”方式,如果該表進(jìn)行了歸檔,當前只剩下5000條記錄了,那查詢(xún)優(yōu)化器就會(huì )改變方案,采用“全表掃描”方式。
可見(jiàn),執行計劃并不是固定的,它是“個(gè)性化的”。產(chǎn)生一個(gè)正確的“執行計劃”有兩點(diǎn)很重要:
(1)SQL語(yǔ)句是否清晰地告訴查詢(xún)優(yōu)化器它想干什么?
(2)查詢(xún)優(yōu)化器得到的數據庫統計信息是否是最新的、正確的?
2、統一SQL語(yǔ)句的寫(xiě)法
對于以下兩句SQL語(yǔ)句,程序員認為是相同的,數據庫查詢(xún)優(yōu)化器認為是不同的。
select * from dual
select * From dual
其實(shí)就是大小寫(xiě)不同,查詢(xún)分析器就認為是兩句不同的SQL語(yǔ)句,必須進(jìn)行兩次解析。生成2個(gè)執行計劃。所以作為程序員,應該保證相同的查詢(xún)語(yǔ)句在任何地方都一致,多一個(gè)空格都不行!
3、不要把SQL語(yǔ)句寫(xiě)得太復雜
我經(jīng)?吹,從數據庫中捕捉到的一條SQL語(yǔ)句打印出來(lái)有2張A4紙這么長(cháng)。一般來(lái)說(shuō)這么復雜的語(yǔ)句通常都是有問(wèn)題的。我拿著(zhù)這2頁(yè)長(cháng)的SQL語(yǔ)句去請教原作者,結果他說(shuō)時(shí)間太長(cháng),他一時(shí)也看不懂了?上攵,連原作者都有可能看糊涂的SQL語(yǔ)句,數據庫也一樣會(huì )看糊涂。
一般,將一個(gè)Select語(yǔ)句的結果作為子集,然后從該子集中再進(jìn)行查詢(xún),這種一層嵌套語(yǔ)句還是比較常見(jiàn)的,但是根據經(jīng)驗,超過(guò)3層嵌套,查詢(xún)優(yōu)化器就很容易給出錯誤的執行計劃。因為它被繞暈了。像這種類(lèi)似人工智能的東西,終究比人的分辨力要差些,如果人都看暈了,我可以保證數據庫也會(huì )暈的。
另外,執行計劃是可以被重用的,越簡(jiǎn)單的SQL語(yǔ)句被重用的可能性越高。而復雜的SQL語(yǔ)句只要有一個(gè)字符發(fā)生變化就必須重新解析,然后再把這一大堆垃圾塞在內存里?上攵,數據庫的效率會(huì )何等低下。
4、使用“臨時(shí)表”暫存中間結果
簡(jiǎn)化SQL語(yǔ)句的重要方法就是采用臨時(shí)表暫存中間結果,但是,臨時(shí)表的好處遠遠不止這些,將臨時(shí)結果暫存在臨時(shí)表,后面的查詢(xún)就在tempdb中了,這可以避免程序中多次掃描主表,也大大減少了程序執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了并發(fā)性能。
5、OLTP系統SQL語(yǔ)句必須采用綁定變量
select * from orderheader where changetime > ‘2010-10-20 00:00:01’
select * from orderheader where changetime > ‘2010-09-22 00:00:01’
以上兩句語(yǔ)句,查詢(xún)優(yōu)化器認為是不同的SQL語(yǔ)句,需要解析兩次。如果采用綁定變量
select * from orderheader where changetime > @chgtime
@chgtime變量可以傳入任何值,這樣大量的類(lèi)似查詢(xún)可以重用該執行計劃了,這可以大大降低數據庫解析SQL語(yǔ)句的負擔。一次解析,多次重用,是提高數據庫效率的原則。
6、綁定變量窺測
事物都存在兩面性,綁定變量對大多數OLTP處理是適用的,但是也有例外。比如在where條件中的字段是“傾斜字段”的時(shí)候。
“傾斜字段”指該列中的絕大多數的值都是相同的,比如一張人口調查表,其中“民族”這列,90%以上都是漢族。那么如果一個(gè)SQL語(yǔ)句要查詢(xún)30歲的漢族人口有多少,那“民族”這列必然要被放在where條件中。這個(gè)時(shí)候如果采用綁定變量@nation會(huì )存在很大問(wèn)題。
試想如果@nation傳入的第一個(gè)值是“漢族”,那整個(gè)執行計劃必然會(huì )選擇表掃描。然后,第二個(gè)值傳入的是“布依族”,按理說(shuō)“布依族”占的比例可能只有萬(wàn)分之一,應該采用索引查找。但是,由于重用了第一次解析的“漢族”的那個(gè)執行計劃,那么第二次也將采用表掃描方式。這個(gè)問(wèn)題就是著(zhù)名的“綁定變量窺測”,建議對于“傾斜字段”不要采用綁定變量。
7、只在必要的情況下才使用begin tran
SQL Server中一句SQL語(yǔ)句默認就是一個(gè)事務(wù),在該語(yǔ)句執行完成后也是默認commit的。其實(shí),這就是begin tran的一個(gè)最小化的形式,好比在每句語(yǔ)句開(kāi)頭隱含了一個(gè)begin tran,結束時(shí)隱含了一個(gè)commit。
有些情況下,我們需要顯式聲明begin tran,比如做“插、刪、改”操作需要同時(shí)修改幾個(gè)表,要求要么幾個(gè)表都修改成功,要么都不成功。begin tran 可以起到這樣的作用,它可以把若干SQL語(yǔ)句套在一起執行,最后再一起commit。好處是保證了數據的一致性,但任何事情都不是完美無(wú)缺的。Begin tran付出的代價(jià)是在提交之前,所有SQL語(yǔ)句鎖住的資源都不能釋放,直到commit掉。
可見(jiàn),如果Begin tran套住的SQL語(yǔ)句太多,那數據庫的性能就糟糕了。在該大事務(wù)提交之前,必然會(huì )阻塞別的語(yǔ)句,造成block很多。
Begin tran使用的原則是,在保證數據一致性的前提下,begin tran 套住的SQL語(yǔ)句越少越好!有些情況下可以采用觸發(fā)器同步數據,不一定要用begin tran。
文章來(lái)源于領(lǐng)測軟件測試網(wǎng) http://kjueaiud.com/