一、產生原因及影響
索引是數據庫引擎中針對表(有時候也針對視圖)建立的特別數據結構,用來幫助查找和整理數據,它的重要性體現在能夠使數據庫引擎快速返回查詢結果。當對索引所在的基礎數據表進行增刪改時,若存儲的數據進行了不適當的跨頁(SQL Server中存儲的最小單位是頁,頁是不可再分的),就會導致索引碎片的產生。隨着索引碎片的不斷增多,查詢響應時間就會變慢,性能也因此而下降。要解決這個問題,可以通過重新生成或重新組織索引來解決。
二、碎片分類
2.1、外部碎片
當索引頁不在邏輯順序上時就會產生外部碎片。索引創建時,索引鍵按照邏輯順序放在一組索引頁上。當新數據插入索引時,新的鍵可能放在存在的鍵之間。為了讓新的鍵按照正確的順序插入,可能會創建新的索引頁來存儲需要移動的那些存在的鍵。這些新的索引頁通常物理上不會和那些被移動的鍵原來所在的頁相鄰。創建新頁的過程會引起索引頁偏離邏輯順序。
2.2、內部碎片
當索引頁沒有用到最大量時就產生了內部碎片。雖然在一個有頻繁數據插入的應用程序里這也許有幫助,然而設置一個fill factor(填充因子)會在索引頁上留下空間,服務器內部碎片會導致索引尺寸增加,從而在返回需要的數據時要執行額外的讀操作。這些額外的讀操作會降低查詢的性能。
三、維護方法
1、刪除索引並重建。
2、使用DROP_EXISTING語句重建索引。
3、使用ALTER INDEX REBUILD重新生成索引。(推薦)
4、使用ALTER INDEX REORGANIZE重新組織索引。(推薦)
四、注意事項
碎片率 | 采用方法 |
>30% | ALTER INDEX REBUILD WITH(ONLINE = ON) |
>5% 且 <=30% | ALTER INDEX REORGANIZE |
重新生成索引可以聯機執行,也可以脫機執行。
重新組織索引始終聯機執行。這些值提供了一個大致指導原則,用於確定應在ALTER INDEX REORGANIZE和ALTER INDEX REBUILD之間進行切換的點。不過,實際值可能會隨情況而變化,必須要通過試驗來確定最適合您環境的閾值。
非常低的碎片級別(小於5%)不應通過這些命令來解決,因為刪除如此少量的碎片所獲得的收益始終遠低於重新生成或重新組織索引的開銷。
切記:所有索引碎片維護一定要在凌晨(非業務高峰期間)進行!!!
五、優化指導原則
5.1、如何知道是否發生了索引碎片?
在SQL Server數據庫中,可以通過DBCC SHOWCONTIG WITH ALL_INDEXES或DBCC SHOWCONTIG(表ID或者表名) WITH ALL_INDEXES來檢查索引碎片情況。
--方法一 --目標數據庫 USE DB_NAME --創建變量指定要查看的表 DECLARE @TABLE_ID INT SET @TABLE_ID=OBJECT_ID('TABLE_NAME') --執行 DBCC SHOWCONTIG(@TABLE_ID) WITH ALL_INDEXES --方法二 USE DB_NAME DBCC SHOWCONTIG('TABLE_NAME') WITH ALL_INDEXES
5.2、索引碎片判斷標准
通過對邏輯掃描碎片(過高)、平均頁密度(滿)(過低)的結果分析,判定是否需要進行索引處理,如下所示:
邏輯掃描碎片 ..................:97.83% 該百分比應該在0%到10%之間,高了則說明有外部碎片。
平均頁密度(滿) ..................:62.42% 該百分比應該盡可能靠近100%,低了則說明有外部碎片。
六、優化實踐
6.1、手動方式
第一步:查詢數據庫所有表的索引信息。
SELECT OBJECT_NAME(B.OBJECT_ID) 表名,B.NAME 索引名稱,A.INDEX_TYPE_DESC 索引類型, ROUND(A.AVG_FRAGMENTATION_IN_PERCENT,2) 碎片率 FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) A INNER JOIN sys.indexes B ON A.OBJECT_ID=B.OBJECT_ID AND A.INDEX_ID=B.INDEX_ID WHERE 1=1 AND A.AVG_FRAGMENTATION_IN_PERCENT>30 --AND A.AVG_FRAGMENTATION_IN_PERCENT>5 AND A.AVG_FRAGMENTATION_IN_PERCENT<=30 ORDER BY OBJECT_NAME(B.OBJECT_ID),A.AVG_FRAGMENTATION_IN_PERCENT DESC
注:通過碎片率,依四、注意事項處理方式,也可以逐個對表的索引進行對應的重新生成或重新組織處理。
第二步:生成數據庫所有表的索引處理的SQL語句。
SELECT OBJECT_SCHEMA_NAME(B.OBJECT_ID) 架構,OBJECT_NAME(B.OBJECT_ID) 表名,B.NAME 索引名,ROUND(A.AVG_FRAGMENTATION_IN_PERCENT,2) 碎片率, CASE WHEN A.AVG_FRAGMENTATION_IN_PERCENT>30 THEN N'重新生成索引' ELSE N'重新組織索引' END 處理方式, 'ALTER INDEX '+QUOTENAME(B.NAME)+' ON '+QUOTENAME(OBJECT_SCHEMA_NAME(B.OBJECT_ID))+'.'+QUOTENAME(OBJECT_NAME(B.OBJECT_ID))+' ' +CASE WHEN A.AVG_FRAGMENTATION_IN_PERCENT>30 THEN 'REBUILD' ELSE 'REORGANIZE' END 生成SQL語句 FROM sys.dm_db_index_physical_stats(DB_ID(),NULL,NULL,NULL,NULL) A INNER JOIN sys.indexes B ON A.OBJECT_ID=B.OBJECT_ID AND A.INDEX_ID=B.INDEX_ID WHERE A.AVG_FRAGMENTATION_IN_PERCENT>5 AND B.INDEX_ID>0 --AND OBJECT_NAME(B.OBJECT_ID) IN ('INVMB') --指定表 ORDER BY CASE WHEN A.AVG_FRAGMENTATION_IN_PERCENT>30 THEN N'重新生成索引' ELSE N'重新組織索引' END,OBJECT_NAME(B.OBJECT_ID),B.INDEX_ID
注:將【生成SQL語句】拷貝出來執行即可。
6.2、自動方式
第一步:在服務中啟動SQL Server 代理。
第二步:點擊"管理"->右鍵"維護計划"->"新建維護計划"。
第三步:起個名字,點擊"確定"。
第四步:點擊左側"工具箱",將"重新生成索引"及"重新組織索引"拖至右邊區域。
第五步:分別對着"重新生成索引"及"重新組織索引"點擊右鍵->"編輯"->在"數據庫"項勾選要處理的數據庫->點擊"確定"。
第六步:點擊"新建作業計划"按鈕->設置頻率及執行時間->點擊"確定"。
第七步:點擊"保存選定項"即可。
七、更新統計信息
作用:UPDATE STATISTICS更新統計信息來提高查詢效率。建議放在索引碎片計划任務執行完成之后進行。
查看:查看某個表的統計信息,可以在SSMS下面查看。
執行:
--方法一:UPDATE STATISTICS 表名 UPDATE STATISTICS INVMB --方法二:執行存儲過程SP_UPDATESTATS(更新所有表) EXEC sp_updatestats
后記:建議不要過於頻繁地執行重新生成、重新組織索引以及更新統計信息。另外需要補充的是,非常低數據量與非常低碎片級別一樣,通過這些命令來解決,效果甚微。