sql優化問題

個人對於sql優化這方面的一些想法,僅供參數,也是結合自己實際開發經驗講解,如有錯誤希望大佬指正.優化顧名思義就是性能發揮到極致,sql優化我從兩方面入手,一方面是設計階段,另一方面就是開發階段,開發階段主要是結合自己的實際情況.


設計階段:


三大範式:

1規範:沒有重複的組或多值的列,這是數據庫設計的最低要求。

2規範:每個非關鍵字段必須依賴於主關鍵字,不能依賴於一個組合式主關鍵字的某些組成部分。消除部分依賴,大部分情況下,數據庫設計都應該達到第二範式。

3規範:一個非關鍵字段不能依賴於另一個非關鍵字段。消除傳遞依賴,達到第三範式應該是系統中大部分表的要求,除非一些特殊作用的表。

更高的範式要求這裏就不再作介紹了,個人認爲,如果全部達到第二範式,大部分達到第三範式,系統會產生較少的列和較多的表,因而減少了數據冗餘,也利於性能的提高。

完全按照三大範式規範化設計的系統幾乎是不可能的,除非系統特別的小,在規範化設計後,有計劃地加入冗餘是必要的。冗餘可以是冗餘數據庫、冗餘表或者冗餘字段,不同粒度的冗餘可以起到不同的作用。冗餘可以是爲了編程方便而增加,也可以是爲了性能的提高而增加。從性能角度來說,冗餘數據庫可以分散數據庫壓力,冗餘表可以分散數據量大的表的併發壓力,也可以加快特殊查詢的速度,冗餘字段可以有效減少數據庫表的連接,提高效率。

主鍵的設計

主鍵是必要的,SQL SERVER的主鍵同時是一個唯一索引,而且在實際應用中,我們往往選擇最小的鍵組合作爲主鍵,所以主鍵往往適合作爲表的聚集索引。聚集索引對查詢的影響是比較大的,這個在下面索引的敘述。

在有多個鍵的表,主鍵的選擇也比較重要,一般選擇總的長度小的鍵,小的鍵的比較速度快,同時小的鍵可以使主鍵的B樹結構的層次更少。

主鍵的選擇還要注意組合主鍵的字段次序,對於組合主鍵來說,不同的字段次序的主鍵的性能差別可能會很大,一般應該選擇重複率低、單獨或者組合查詢可能性大的字段放在前面。

 外鍵的設計

外鍵作爲數據庫對象,很多人認爲麻煩而不用,實際上,外鍵在大部分情況下是很有用的,理由是:

外鍵是最高效的一致性維護方法,數據庫的一致性要求,依次可以用外鍵、CHECK約束、規則約束、觸發器、客戶端程序,一般認爲,離數據越近的方法效率越高。

謹慎使用級聯刪除和級聯更新,級聯刪除和級聯更新作爲SQL SERVER 2000當年的新功能,在2005作了保留,應該有其可用之處。我這裏說的謹慎,是因爲級聯刪除和級聯更新有些突破了傳統的關於外鍵的定義,功能有點太過強大,使用前必須確定自己已經把握好其功能範圍,否則,級聯刪除和級聯更新可能讓你的數據莫名其妙的被修改或者丟失。從性能看級聯刪除和級聯更新是比其他方法更高效的方法。

字段數據類型設計原則:

A、數據類型儘量用數字型,數字型的比較比字符型的快很多,尤其是作爲主鍵。

B數據類型儘量小,這裏的儘量小是指在滿足可以預見的未來需求的前提下的,節省一個字節是一個字節,雖然硬盤便宜也不能浪費啊。

C儘量不要允許NULL,除非必要,可以用NOT NULL+DEFAULT代替。

   爲什麼最好不要爲null,因爲在程序處理過程中,你經常要爲null值進行處理,比如使用isnull進行判斷,這樣削弱查詢的速度,還有程序中需要不斷的爲null值進行判斷,多寫了代碼,減少了程序的性能.

D、少用TEXTIMAGE,二進制字段的讀寫是比較慢的,而且,讀取的方法也不多,大部分情況下最好不用。在sqlserver 2005儘可能使用nvarchar(max),或者varchar(max);除非必要圖片儘量上傳到服務器,數據庫保留上傳地址.

E自增字段要慎用

1.不利於數據遷移.

2.不利於分佈式部署.

3.無法預知Id,爲子表數據插入造成困難.

4.沒有實際意義,無法讓人看出這個數字到底有什麼用.

F、儘可能使用定長數據類型,而不是變長數據類型

爲什麼不要設計過多的變長類型的數據呢?

1.對於 SQl Server爲說,變長類型的數據,在更新的時候,如果長度比以前的大,會進行頁拆分。會對查詢性能造成嚴重的影響。會增加查詢時,O/I的花費 (Cost)頁分隔越多,查詢時,O/I的開銷就越大。對於變長的字段來說,有可能,這個字符的內容,存儲在不同的位置。這個字段的內容,存儲在不同的位置。存儲在不同的頁中,它們之間有指針來關聯。這種情況會造成查詢時,磁頭來回尋址,定位。可能你查一條記錄,磁頭找這條記錄的這個變長字段的內容,都要去好幾個頁裏找,才能完整的找到。這樣,就造成了很大個 O/I開銷,降低了查詢性能。從物理上來說,文件本來就經常容易產生碎片。再加上變長類型的頁拆分。

頁是sql server存儲數據的基本單位,大小爲8kb,可以存儲表數據、索引數據、執行計劃數據、分配位圖、可用空間信息。頁是sql server可以讀寫的最小I/O單位。即便是讀取一行數據,它也要把整個頁加載到緩存並從緩存中讀取數據。

頁拆分是這樣產生的:

比如:有一個變長類型的字段 Content: nvarchar(512).你添加一條記錄,給 Content 的值是 N'ABC',那麼,存儲的時候,直接就存儲 N'ABC'了。當你下次 Update這條記錄的 Content字段時,給的值是 N'ABCDEF'那麼就會發生頁拆分。DEF對被存儲在其它頁。因爲有可能上一次分配的數據頁已經存儲了其他行的數據對吧,對,512 ,只是用來限制這個字段的長度。並不與頁拆分有關係。記錄的物理順序,與你 INSERT的順序是一致。你 INSERT N條,然後再去修改第一條,這時候可能不在同一個頁了.

以上結論就是把變長字段的內容加大,就會造成頁拆分了。也就是說可變長類型是把一頁填滿,再填另一,影響比較大的是,每次insert的時候會增加分配數據頁得次數

當然有可能造成一行數據保存在2個數據頁裏.但是,同樣,不但頁拆分對增加查詢時的 O/I 開銷,字符不必要的太長,也會增加 O/I開銷。

2.字段大小對錶總大小有影響

SQL Server 2005單行字段總長是8060字節

3.可變長類型是有長度限制的

 以上內容是第一篇,請大家多多發表意見!

 

 

 開發階段:

 Sql語句本身:百萬級的數據的sql優化

 

1.對查詢進行優化,要儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。


2.應儘量避免在 where 子句中對字段進行 null值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num isnull

最好不要給數據庫留NULL儘可能的使用 NOT NULL填充數據庫.

備註、描述、評論之類的可以設置爲 NULL,其他的,最好不要使用NULL。

不要以爲 NULL 不需要空間,比如:char(100) 型,在字段建立時,空間就固定了, 不管是否插入值(NULL也包含在內),都是佔用 100個字符的空間的,如果是varchar這樣的變長字段, null 不佔用空間。


可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:

select id from t where num =0


3.應儘量避免在 where 子句中使用 != <>操作符,否則將引擎放棄使用索引而進行全表掃描。

4.應儘量避免在 where 子句中使用 or來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10or Name ='admin'

可以這樣查詢:

select id from t where num = 10unionallselect id from t where Name ='admin'


5.in not in也要慎用,否則會導致全表掃描,如:

select id from t where num in(1,2,3)

對於連續的數值,能用 between就不要用 in

select id from t where num between1and3

很多時候用 exists 代替 in 是一個好的選擇:

select num from a where num in(select num from b)

用下面的語句替換:

select num from a whereexists(select1from b where num=a.num)

 

6.下面的查詢也將導致全表掃描:

select id from t where name like%abc%

若要提高效率,可以考慮全文檢索。

7.如果在 where 子句中使用參數,也會導致全表掃描。因爲SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作爲索引選擇的輸入項。如下面語句將進行全表掃描:

select id from t where num = @num

可以改爲強制查詢使用索引:

select id from t with(index(索引名)) where num = @num

8.應儘量避免在 where子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t where num/2 = 100

應改爲:

select id from t where num = 100*2


9.應儘量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:

select id from t wheresubstring(name,1,3) = ’abc’       -–nameabc開頭的idselect id from t wheredatediff(day,createdate,’2005-11-30′) = 0    -–‘2005-11-30’    --生成的id

應改爲:

select id from t where name like'abc%'select id from t where createdate >='2005-11-30'and createdate <'2005-12-1'


10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

11.在使用索引字段作爲條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓字段順序與索引順序相一致。

12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:

select col1,col2 into #t from t where1=0

這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
create table #t(…)

13.Update 語句,如果只更改1、2個字段,不要Update全部字段,否則頻繁調用會引起明顯的性能消耗,同時帶來大量日誌。

14.對於多張大數據量(這裏幾百條就算大了)的表JOIN,要先分頁再JOIN,否則邏輯讀會很高,性能很差。

15.select count(*) from table;這樣不帶任何條件的count會引起全表掃描,並且沒有任何業務意義,是一定要杜絕的。


16.索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。

17.應儘可能的避免更新 clustered 索引數據列,因爲 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建爲 clustered 索引。

18.儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因爲引擎在處理查詢和連 接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。

19.儘可能的使用 varchar/nvarchar代替 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。

20.任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段

21.儘量使用表變量來代替臨時表。如果表變量包含大量數據,請注意索引非常有限(只有主鍵索引)。

22. 避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。臨時表並不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重複引用大型表或常用表中的某個數據集時。但是,對於一次性事件, 最好使用導出表。

23.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,爲了緩和系統表的資源,應先create table,然後insert。

24.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。

25.儘量避免使用遊標,因爲遊標的效率較差,如果遊標操作的數據超過1萬行,那麼就應該考慮改寫。

26.使用基於遊標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。

27.與臨時表一樣,遊標並不是不可使用。對小型數據集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括“合計”的例程通常要比使用遊標執行的速度快。如果開發時 間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。

28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。

29.儘量避免大事務操作,提高系統併發能力

30.儘量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

 

 

 

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