規劃和管理索引
目錄
(1)、使用SQL Server Management Studio創建索引
正文
一、概述
索引是對錶的一列或多列值進行排序的結構。
每個索引都有一個特定的搜索碼與表中的記錄關聯。索引按順序存儲搜索碼的值。
1、數據的存儲和訪問方式
(1)數據的存儲方式
● 每個表的數據存儲在數據頁的集合中。
● 數據頁無序存放。表中的行在數據頁中也是無序存放的。
(2)數據的訪問方式
● 使用表掃面訪問數據:
通過遍歷表中的所有數據查找滿足條件的行。
● 使用索引掃描訪問數據。
通過索引查找滿足條件的行。
2、SQL Server 查詢優化器
● 動態調整數據的訪問方式
● 總能針對數據庫的狀態生成一個最佳的執行計劃。
3、獲得執行計劃的信息
(1)、以圖形方式顯示執行計劃
閱讀執行計劃的方法是從右到左、從上到下閱讀。
示例:
(2)、SET SHOWPLAN_ALL ON
執行該語句,不執行T-SQL查詢,只返回有關語句執行情況的詳細信息,並估計語句對資源的需求。
示例代碼:
SET SHOWPLAN_ALL ON
GO
SELECT * FROM Customer c
INNER JOIN Province p
ON c.provinceID=p.ID
(3)、SET SHOWPLAN_Text ON
該語句與 SET SHOWPLAN_ALL ON 相似,單輸出格式簡潔。
二、索引的類型
1、聚集索引
(1)、結構簡介
聚集索引指示表中數據行按索引鍵的排序次序存儲。
(2)、特點
● 每個表只能有一個聚集索引。
● 聚集索引改變數據的物理排序方式,使得數據行的物理順序和索引中的鍵值順序一致。
● 聚集索引的大小根據被索引的列的情況有所不同。
● 在創建索引時,SQL Server暫時使用當前數據庫的磁盤空間作爲工作空間,約爲被索引的表的1.2倍,這些磁盤空間在創建索引後被自動收回。
2、非聚集索引
(1)、結構簡介
非聚集索引具有完全獨立於數據行的結構。數據行不按索引鍵的次序存儲。
(2)、特點
● 如果創建索引時沒有指定索引的類型,默認情況下爲非聚集索引。
● 應當在創建非聚集索引之前創建聚集索引。
● 每個表最多可以創建249個非聚集索引。
● 最好在唯一值較多的列上創建非聚集索引。
三、使用索引的準則
1、創建索引的列
(1)、主關鍵字所在的列
主鍵列包含唯一值,利用主鍵能夠迅速完成單行查找,而且主關鍵字還經常用來作爲連接查詢中與其他表進行關聯的條件。
(2)、外部關鍵字所在的列或在連接查詢中經常使用的列
使用JOIN子句進行連接查詢時,需要使用外部關鍵字與父表進行關聯。
(3)、按關鍵字的範圍值進行搜索的列
查詢中的where子句可以指定按某個關鍵字的範圍值搜索數據。
如使用:between and 、>、>=、<和<=。
(4)、按關鍵字的排序順序訪問的列
經常使用order by子句對數據按某個順序進行排序的列。
使用group by進行分組的列。
2、不建索引的列
(1)、在查詢中很少涉及的列
大量使用索引將佔用更多的磁盤空間,SQL Server維護索引將影響系統的性能。對於查詢中幾乎不會使用到的列,不需要創建索引。
(2)、包含較少的唯一值
在具有大量重複值的列(如:性別)進行索引時,返回的行在所有的行中佔了較高的百分比。在這種情況下,使用索引檢索數據反而會比使用表掃描檢索數據時的性能更差。
(3)、更新性能比查詢性能更重要的列
在被索引的列上修改數據時,SQL Server將更新相關的索引,維護索引需要更多的時間和資源開銷。在保證表的更新性更重要的情況下,不要在列上使用索引,否則將導致過多的維護開銷,影響系統性能。
(4)、由text、ntext、image數據類型定義的列
在SQL Server中,使用大對象數據類型的列不能被索引。
四、創建和管理索引
1、創建索引
(1)、使用SQL Server Management Studio創建索引
在表上右鍵選擇“設計”,在列上右鍵選擇“索引/鍵”,在“索引/鍵”窗口點擊“添加”按鈕,將產生一個新的“主/唯一鍵或索引”。如下圖所示,編輯相應的屬性(是否唯一,是否是聚集索引等)後關閉該窗口,對錶的更改進行保存。
(2)、使用T-SQL語句創建索引
● 創建索引的基本語法如下:
CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED] INDEX index_name
ON {table_name | view_name}
(column_name [ASC | DESC] [,…n])
[WITH FILLFACTOR = fillfactor,IGNORE_DUP_KEY,DROP_EXISTING]
● 相關參數說明
參數 |
說明 |
UNIQUE |
爲表或視圖創建唯一索引(不允許存在索引值相同的兩行)。 |
CLUSTERED |
創建聚集索引。每個表中只能允許有一個聚集索引。如果沒有指定CLUSTERED,則創建非聚集索引。 |
NONCLUSTERED |
創建非聚集索引,非聚集索引最多可以創建249個。 |
index_name |
索引名。索引名在表中必須唯一,但在數據庫中不必唯一。 |
table_name |
包含要創建索引的列的表。只有表的所有者可以在表中創建索引 |
view_name |
要建立索引的視圖的名稱。視圖索引的類型必須是唯一聚集索引。必須使用SCHEMABINDING定義視圖才能在視圖上創建索引。而且,索引中所有鍵列必須是精確的,不能在視圖的float列上或者使用非確定性函數表達式的列上創建索引。 |
column_name |
應用索引的列。可以指定一個或多個列名,當指定多個列名時,將使用這些列的組合值創建組合索引。在table_name後的圓括號中列出組合索引中要包含的列(按排序優先級排列)。 |
ASC | DESC |
確定具體某個索引列的升序或降序排序方向,默認設置爲ASC。 |
FILLFACTOR |
爲索引指定填充因子,用來提高表的更新性能。 |
IGNORE_DUP_KEY |
如果爲索引指定了IGNORE_DUP_KEY,當INSERT語句向唯一索引列插入重複的鍵值時,SQL Server將發出警告消息並忽略重複的行。 |
DROP_EXISTING |
指定應除去並重建已命名的先前存在的聚集索引或非聚集索引。指定的索引名必須與現有的索引名相同。 |
● 示例代碼
--在Student表的ID上創建非聚集索引
IF EXISTS(SELECT * FROM sysindexes s WHERE s.[name]='IX_Student_ID')
DROP INDEX IX_Student_ID ON Student
GO
CREATE INDEX IX_Student_ID
ON Student(ID)
GO
--在Student表的Name上創建唯一的非聚集索引
IF EXISTS(SELECT * FROM sysindexes s WHERE s.[name]='IX_Student_Name')
DROP INDEX Student.IX_Student_Name
GO
CREATE UNIQUE INDEX IX_Student_Name
ON Student([Name])
GO
--在Student表的firstName和lastName上創建組合索引
IF EXISTS(SELECT * FROM sysindexes s WHERE s.[name]='IX_Student_FirstName_LastName')
DROP INDEX IX_Student_FirstName_LastName ON Student
GO
CREATE INDEX IX_Student_FirstName_LastName
ON Student(firstName,lastName)
GO
--使用DROP_EXISTING選項在Province表的[National]列上刪除並重新創建現有索引。
CREATE NONCLUSTERED INDEX IX_Province_National
ON Province([National])
WITH (DROP_EXISTING=ON,FILLFACTOR=80)
GO
(3)、注意事項
● 在創建索引前考慮表中已經創建的索引數量,最好不要在一個表中創建大量的索引。
● 檢查表中已經創建的索引定義,避免對同一列重疊創建索引。
● 選擇在唯一值較高的列上創建索引。檢查列中唯一數據值的數量,並與表中的行數進行比較,比較的結果就是該列的可選性。
● 一個索引包含的所有列的寬度之和不能超過900字節。例如:不能在寬度爲char(300)、char(300)和char(301)的3個列上創建組合索引,因爲總寬度超過了900字節。
2、組合索引
組合索引是指使一個索引中包含了一個以上的列,最多可以有16列組合到一個索引中。
3、唯一索引
● 唯一索引可以確保索引列中不包含重複的值。只有當唯一性是數據本身的特性時,指定唯一索引纔有意義。
● 當創建或修改唯一索引時,可以設置選項(IGNORE_DUP_KEY)以忽略重複的鍵。如果已設置該選項,並且試圖通過添加或更新將影響多行的數據來創建重複鍵(使用INSERT或UPDATE語句),則不能添加導致重複的行,或在更新時放棄這樣的行。
例如:在已經含有“Jones”的表中試圖用“Jones”更新“Smith”,則在結果表中只有一個“Jones”,而沒有“Smith”。原有的“Smith”行丟失,因爲UPDATE語句實際上是DELETE語句後跟一個INSERT語句。“Smith”已刪除,而試圖插入列一個“Jones”的操作失敗。整個事務不能回滾,因爲該選項的目的是允許事務,而不管是否存在重複項。
4、主鍵索引
在SQL Server中爲表定義主鍵時將自動創建主鍵索引,主鍵索引是唯一索引的特殊類型。如果該表尚未創建聚集索引,並且在創建PRIMARY KEY約束時未指定索引的類型,則PRIMARY KEY約束會自動創建聚集索引。
5、填充因子FILLFACTOR
● 頁拆分:如果向已滿的索引頁添加新行,數據庫引擎將把大約一半的行移到新頁中,一遍爲該新行騰出空間。這種重組稱爲頁拆分。頁拆分可爲新行騰出空間,但是執行頁拆分可能需要花費一定的時間,此操作會消耗大量資源。此外,它還可能造成碎片,從而導致IO操作增加。
● 填充因子指定使用現有數據創建新索引時將每頁填滿到什麼程度。頁面不會維護在任何特定的填充水平上。
● 填充因子的值是從0到100的百分比數值,默認爲0。值爲0並不表示頁面的填滿程度爲0%,而是類似於設置爲100的情況,在索引頁中預留很少量的空閒空間。
● 指定填充因子選項是用於微調性能。創建索引時,很少需要指定填充因子。
6、管理索引
(1)、查看索引
可以通過SQL Server Management Studio查看。
也可以使用系統存儲過程sp_helpindex查看錶上或視圖上索引的信息。
示例:
EXEC sp_helpindex 'Student'
(2)、修改索引
修改索引的過程是首先刪除表中準備修改的索引,然後使用新的索引定義重新創建該索引。
創建和修改索聚集索引時,SQL Server要在磁盤上對錶的行進行重組,當表中存儲了大量記錄時,會產生很大的開銷,可能要花很長時間。
(3)、刪除索引
刪除索引可以回收佔用的磁盤空間。
刪除聚集索引會導致重建同一個表上的所有非聚集索引。
可以使用SQL Server Management Studio刪除索引。
也可以使用命令刪除索引。
DROP INDEX Province.IX_Province_Name
若要刪除爲實現Primary Key或UNIQUE約束而創建的索引,必須首先刪除約束。
五、維護索引
創建索引後,必須對索引進行維護,確保索引的統計信息是有效的,才能夠提高查找速度。隨着更新操作不斷執行,數據會變的支離破碎,這些數據碎片會導致額外的頁讀取,妨礙數據的並行掃描。應該定期整理索引清除數據碎片,提高數據讀取的性能。
1、維護索引的統計信息
● 使用DBCC SHOW_STATISTICS命令顯示指定索引的當前分步統計
DBCC SHOW_STATISTICS ('Province',IX_Province_Name)
● 使用UPDATE STATISTICS命令更新表或者視圖中的索引統計信息
UPDATE STATISTICS Province
● 使用sp_updatestats系統存儲過程,對當前數據庫中所有用戶定義的表運行UPDATE STATISTICS
EXEC sp_updatestats
2、數據碎片
● 使用DBCC SHOWCONTIG命令顯示指定的表或視圖的數據和索引的碎片信息
DBCC SHOWCONTIG('Province',IX_Province_Name)
結果集如下圖所示:
結果集內的信息如下表:
統計信息 |
說明 |
掃描頁數 |
表或索引中的頁數 |
掃描區數 |
表或索引中的區數 |
區切換次數 |
遍歷表或索引的頁時,DBCC語句從一個區移動到另一個區的次數 |
每個區的平均頁數 |
頁鏈中每個區的頁數 |
掃描密度[最佳計數:實際計數] |
百分比。這是“最佳計數”與“實際計數”的比率。如果所有內容都是連續的,則該值爲100;如果該值小於100,則存在一些碎片。“最佳計數”是指在一切都連續鏈接的情況下,區更改的理想數目。“實際計數”是指區更改的實際次數。 |
邏輯掃描碎片 |
掃描索引的葉級頁時返回的出錯頁的百分比。此數與堆無關。對於出錯頁,在IAM中所指示的下一頁與葉級頁中下一頁指針所指向的頁不同。 |
區掃描碎片 |
掃描索引的葉級頁時出錯區所佔的百分比。此數與堆無關。對於出錯區,包含當前索引頁的區在物理上不是包含上一個索引頁的區的下一個區。 注意:如果索引跨越多個文件,則此數字無意義。 |
每頁的平均可用字節數 |
掃描的頁上平均可用字節數。此數字越大,則頁的填充程度越低。如果索引不會有很多隨機插入,則數字越小越好。此數字還受行大小影響,行越大,此數字就越大。 |
平均頁密度(滿) |
頁的平均密度,以百分比表示。該值會考慮行大小。因此,該值可以更準確地指示頁的填充程度。百分比越大越好。 |
● 索引的碎片級可以以兩種方式確定。
(1)比較“區切換次數”和“掃描區數”的值。
“區切換次數”的值應儘可能接近於“掃描區數”的值。此比率將作爲“掃描密度”值計算。此值應儘可能的大,可通過減少索引碎片得到改善。如果索引跨多個數據文件,這種確定碎片的方法不起作用。
(2)瞭解“邏輯掃描碎片”和“區掃描碎片”的值
“邏輯掃描碎片”和“區掃描碎片”(對於較小的區)的值是表的碎片級別的最好指示。這兩個值應儘可能接近零,但0%~10%的值都是可接受的。
注意:如果索引涉及多個文件,則“區掃描碎片”的值將較高。若要減小這些值,必須減少索引碎片。
3、重建和整理索引
(1)、刪除並重新創建聚集索引
● 刪除表上的聚集索引,隨後重新創建聚集索引,將對數據進行重新組織,能夠使數據頁填滿,並且除去數據碎片。索引的填滿程度可以使用FILLFACTOR選項進行配置。
● 這種方法的缺點是索引在刪除和重新創建的週期內不能工作,創建聚集索引時,需要分析數據並對數據的物理存儲順序進行排序,對系統資源造成很大開銷,如果中斷創建索引的操作,那麼已完成的工作將被撤銷。
● 使用刪除舊索引然後重新創建同一索引的方式重建聚集索引是一種昂貴的方法。刪除聚集索引將導致重建所有非聚集索引,如果此後重新創建聚集索引,將再次重建非聚集索引。所以,使用這個方法重建聚集索引,將導致所有非聚集索引都被刪除和重新創建兩次。
(2)、DBCC INDEXDEFRAG命令
● 通過使用INDEXDEFRAG語句清理索引碎片,不必單獨重建每個索引。
例如:整理TestDB數據庫中Province表的IX_Province_Name索引碎片。
DBCC INDEXDEFRAG (TestDB,Province,IX_Province_Name)
● 整理碎片過程中允許用戶中斷該操作,而且已完成工作仍然有效。
● 這種方法的缺點是在重新組織數據方面沒有刪除/重建聚集索引的方式有效。如果索引的碎片相對較少,則整理該索引的速度比生成一個新索引要快。但是,對碎片太多的索引進行整理可能要比重建索引花更多的時間。
(3)、DROP_EXISTING子句
CREATE INDEX的WITH DROP_EXISTING子句可以對重建索引工作進行優化,用一個步驟重新創建索引,以避免重建兩次非聚集索引的開銷。
--使用DROP_EXISTING選項在Province表的[National]列上刪除並重新創建現有索引。
CREATE NONCLUSTERED INDEX IX_Province_National
ON Province([National])
WITH (DROP_EXISTING=ON,FILLFACTOR=80)
GO