索引的重要性
數據庫性能優化中索引絕對是一個重量級的因素,能夠說,索引使用不當,其它優化措施將毫無心義。
聚簇索引(Clustered Index)和非聚簇索引 (Non- Clustered Index)
最通俗的解釋是:聚簇索引的順序就是數據的物理存儲順序,而對非聚簇索引的索引順序與數據物理排列順序無關。舉例來講,你翻到新華字典的漢字「爬」那一頁就是P開頭的部分,這就是物理存儲順序(聚簇索引);而不用你到目錄,找到漢字「爬」所在的頁碼,而後根據頁碼找到這個字(非聚簇索引)。
下表給出了什麼時候使用聚簇索引與非聚簇索引:數據庫
動做 |
使用聚簇索引 |
使用非聚簇索引 |
列常常被分組排序 |
應 |
應 |
返回某範圍內的數據 |
應 |
不該 |
一個或極少不一樣值 |
不該 |
不該 |
小數目的不一樣值 |
應 |
不該 |
大數目的不一樣值 |
不該 |
應 |
頻繁更新的列 |
不該 |
應 |
外鍵列 |
應 |
應 |
主鍵列 |
應 |
應 |
頻繁修改索引列 |
不該 |
應 |
聚簇索引的惟一性
正式聚簇索引的順序就是數據的物理存儲順序,因此一個表最多隻能有一個聚簇索引,由於物理存儲只能有一個順序。正由於一個表最多隻能有一個聚簇索引,因此它顯得更爲珍貴,一個表設置什麼爲聚簇索引對性能很關鍵。
初學者最大的誤區:把主鍵自動設爲聚簇索引
由於這是SQLServer的默認主鍵行爲,你設置了主鍵,它就把主鍵設爲聚簇索引,而一個表最多隻能有一個聚簇索引,因此不少人就把其餘索引設置爲非聚簇索引。這個是最大的誤區。甚至有的主鍵又是無心義的自動增量字段,那樣的話Clustered index對效率的幫助,徹底被浪費了。
剛纔說到了,聚簇索引性能最好並且具備惟一性,因此很是珍貴,必須慎重設置。通常要根據這個表最經常使用的SQL查詢方式來進行選擇,某個字段做爲聚簇索引,或組合聚簇索引,這個要看實際狀況。
事實上,建表的時候,先須要設置主鍵,而後添加咱們想要的聚簇索引,最後設置主鍵,SQLServer就會自動把主鍵設置爲非聚簇索引(會自動根據狀況選擇)。若是你已經設置了主鍵爲聚簇索引,必須先刪除主鍵,而後添加咱們想要的聚簇索引,最後恢復設置主鍵便可。
記住咱們的最終目的就是在相同結果集狀況下,儘量減小邏輯IO。
咱們先從一個實際使用的簡單例子開始。
一個簡單的表:函數
CREATE TABLE [dbo].[Table1](
工具
[ID] [int] IDENTITY(1,1) NOT NULL,
oop
[Da性能
ta1] [int] NOT NULL DEFAULT ((0)),
測試
[Da字體
ta2] [int] NOT NULL DEFAULT ((0)),
優化
[Daspa
ta3] [int] NOT NULL DEFAULT ((0)),
.net
[Name1] [nvarchar](50) NOT NULL DEFAULT (''),
[Name2] [nvarchar](50) NOT NULL DEFAULT (''),
[Name3] [nvarchar](50) DEFAULT (''),
[DTAt] [datetime] NOT NULL DEFAULT (getdate())
複製代碼
declare @i int
set @i = 1
while @i < 100000
begin
insert into Table1 ([Da
ta1] ,[Da
ta2] ,[Da
ta3] ,[Name1],[Name2] ,[Name3])
values(@i , 2* @i ,3*@i, CAST(@i AS NVARCHAR(50)), CAST(2*@i AS NVARCHAR(50)), CAST(3*@i AS NVARCHAR(50)))
set @i = @i + 1
end
update table1 set dtat= DateAdd (s, da
ta1, dtat)
複製代碼
打開查詢分析器的IO統計和時間統計:
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
複製代碼
顯示實際的「執行計劃」:
咱們最經常使用的SQL查詢是這樣的:
SELECT * FROM Table1 WHERE Da
ta1 = 2 ORDER BY DTAt DESC;
複製代碼
先在Table1設主鍵ID,系統自動爲該主鍵創建了聚簇索引。
而後執行該語句,結果是:
Table 'Table1'. Scan count 1, logical reads 911, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 7 ms.
複製代碼
CREATE NONCLUSTERED INDEX [N_Da
ta1] ON [dbo].[Table1]
(
[Da
ta1] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ON
LINE = OFF) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [N_DTat] ON [dbo].[Table1]
(
[DTAt] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ON
LINE = OFF) ON [PRIMARY]
複製代碼
再次執行該語句,結果是:
Table 'Table1'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 39 ms.
複製代碼
能夠看到設立了索引反而沒有任何性能的提高並且消耗的時間更多了,繼續調整。
而後咱們刪除全部非聚簇索引,並刪除主鍵,這樣全部索引都刪除了。創建組合索引Data1和DTAt,最後加上主鍵:
CREATE CLUSTERED INDEX [C_Da
ta1_DTat] ON [dbo].[Table1]
(
[Da
ta1] ASC,
[DTAt] ASC
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ON
LINE = OFF) ON [PRIMARY]
複製代碼
再次執行語句:
Table 'Table1'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
複製代碼
能夠看到只有聚簇索引seek了,消除了index scan和nested loop,並且執行時間也只有1ms,達到了最初優化的目的。
組合索引小結
小結以上的調優實踐,要注意聚簇索引的選擇。首先咱們要找到咱們最多用到的SQL查詢,像本例就是那句相似的組合條件查詢的狀況,這種狀況最好使用組合聚簇索引,並且最多用到的字段要放在組合聚簇索引的前面,不然的話就索引就不會有好的效果,看下例:
查詢條件落在組合索引的第二個字段上,引發了index scan,效果很很差,執行時間是:
Table 'Table1'. Scan count 1, logical reads 238, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 22 ms.
複製代碼
而若是僅查詢條件是第一個字段也沒有問題,由於組合索引最左前綴原則,實踐以下:
Table 'Table1'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
複製代碼
從中能夠看出,最多用到的字段要放在組合聚簇索引的前面。
Index seek 爲何比 Index scan好?
索引掃描也就是遍歷B樹,而seek是B樹查找直接定位。
Index scan多半是出如今索引列在表達式中。數據庫引擎沒法直接肯定你要的列的值,因此只能掃描整個整個索引進行計算。index seek就要好不少.數據庫引擎只須要掃描幾個分支節點就能夠定位到你要的記錄。回過來,若是彙集索引的葉子節點就是記錄,那麼Clustered Index Scan就基本等同於full table scan。
一些優化原則
一、缺省狀況下創建的索引是非聚簇索引,但有時它並非最佳的。在非羣集索引下,數據在物理上隨機存放在數據頁上。合理的索引設計要創建在對各類查詢的分析和預測上。通常來講:
a.有大量重複值、且常常有範圍查詢( > ,< ,> =,< =)和order by、group by發生的列,可考
慮創建羣集索引;
b.常常同時存取多列,且每列都含有重複值可考慮創建組合索引;
c.組合索引要儘可能使關鍵查詢造成索引覆蓋,其前導列必定是使用最頻繁的列。索引雖有助於提升性能但不是索引越多越好,剛好相反過多的索引會致使系統低效。用戶在表中每加進一個索引,維護索引集合就要作相應的更新工做。
二、ORDER BY和GROPU BY使用ORDER BY和GROUP BY短語,任何一種索引都有助於SELECT的性能提升。
三、多表操做在被實際執行前,查詢優化器會根據鏈接條件,列出幾組可能的鏈接方案並從中找出系統開銷最小的最佳方案。鏈接條件要充份考慮帶有索引的表、行數多的表;內外表的選擇可由公式:外層表中的匹配行數*內層表中每一次查找的次數肯定,乘積最小爲最佳方案。
四、任何對列的操做都將致使表掃描,它包括數據庫函數、計算表達式等等,查詢時要儘量將操做移至等號右邊。
五、IN、OR子句常會使用工做表,使索引失效。若是不產生大量重複值,能夠考慮把子句拆開。拆開的子句中應該包含索引。
Sql的優化原則2:
一、只要能知足你的需求,應儘量使用更小的數據類型:例如使用MEDIUMINT代替INT
二、儘可能把全部的列設置爲NOT NULL,若是你要保存NULL,手動去設置它,而不是把它設爲默認值。
三、儘可能少用VARCHAR、TEXT、BLOB類型
四、若是你的數據只有你所知的少許的幾個。最好使用ENUM類型
使用SQLServer Profiler找出數據庫中性能最差的SQL
首先打開SQLServer Profiler:
而後點擊工具欄「New Trace」,使用默認的模板,點擊RUN。
也許會有報錯:"only TrueType fonts are supported. There id not a TrueType font"。不用怕,點擊Tools菜單->Options,從新選擇一個字體例如Vendana 便可。(這個是微軟的一個bug)
運行起來之後,SQLServer Profiler會監控數據庫的活動,因此最好在你須要監控的數據庫上多作些操做。等以爲差很少了,點擊中止。而後保存trace結果到文件或者table。
這裏保存到Table:在菜單「File」-「Save as 」-「Trace table」,例如輸入一個master數據庫的新的table名:profileTrace,保存便可。
找到最耗時的SQL:
use master
select * from profiletrace order by duration desc;
複製代碼
找到了性能瓶頸,接下來就能夠有針對性的一個個進行調優了。
對使用SQLServer Profiler的更多信息能夠參考:
http://www.codeproject.com/KB/database/DiagnoseProblemsSQLServer.aspx
使用SQLServer Database Engine Tuning Advisor數據庫引擎優化顧問
使用上述的SQLServer Profiler獲得了trace還有一個好處就是能夠用到這個優化顧問。用它能夠偷點懶,獲得SQLServer給您的優化顧問,例如這個表須要加個索引什麼的…
首先打開數據庫引擎優化顧問:
而後打開剛纔profiler的結果(咱們存到了master數據庫的profileTrace表):
點擊「start analysis」,運行完成後查看優化建議(圖中最後是建議創建的索引,性能提高72%)
這個方法能夠偷點懶,獲得SQLServer給您的優化顧問。