sql 性能優化 索引碎片


1、索引

簡單的說,索引就像書本的目錄,目錄可以快速找到所在頁數,數據庫中索引可以幫助快速找到數據,而不用全表掃描,合適的索引可以大大提高數據庫查詢的效率。
(1). 優點
大大加快了數據庫檢索的速度,包括對單表查詢、連表查詢、分組查詢、排序查詢。經常是一到兩個數量級的性能提升,且隨着數據數量級增長。

 

(2). 缺點
索引的創建和維護存在消耗,索引會占用物理空間,且隨着數據量的增加而增加。
在對數據庫進行增刪改時需要維護索引,所以會對增刪改的性能存在影響。

a. 直接創建索引和間接創建索引
直接創建: 使用sql語句創建,Android中可以在SQLiteOpenHelper的onCreate或是onUpgrade中直接excuSql創建語句,語句如

間接創建: 定義主鍵約束或者唯一性鍵約束,可以間接創建索引,主鍵默認為唯一索引。

 

b. 普通索引和唯一性索引
普通索引:

唯一性索引:保證在索引列中的全部數據是唯一的,對聚簇索引和非聚簇索引都可以使用,語句為

c. 單個索引和復合索引

單個索引:索引建立語句中僅包含單個字段,如上面的普通索引和唯一性索引創建示例。
復合索引:又叫組合索引,在索引建立語句中同時包含多個字段,語句如:

CREATE INDEX name_index ON username(firstname, lastname)

其中firstname為前導列。

 

d. 聚簇索引和非聚簇索引(聚集索引,群集索引)
聚簇索引:物理索引,與基表的物理順序相同,數據值的順序總是按照順序排列,語句為:

其中WITH ALLOW_DUP_ROW表示允許有重復記錄的聚簇索引

非聚簇索引:

索引默認為非聚簇索引

 

(4). 使用場景
在上面講到了優缺點,那么肯定會對何時使用索引既有點明白又有點糊塗吧,那么下面總結下:
a.  當某字段數據更新頻率較低,查詢頻率較高,經常有范圍查詢(>, <, =, >=, <=)或order by、group by發生時建議使用索引。並且選擇度越大,建索引越有優勢,這里選擇度指一個字段中唯一值的數量/總的數量。
b.  經常同時存取多列,且每列都含有重復值可考慮建立復合索引

、查看數據庫表中索引的情況

select i.name AS '索引名稱' ,o.name AS TableName,avg_fragmentation_in_percent,*  
FROM  sys.dm_db_index_physical_stats(DB_ID() ,object_id('agent') ,NULL,NULL,NULL)
inner join sys.indexes i on i.object_id=dm_db_index_physical_stats.object_id
INNER JOIN sys.objects o ON o.object_id = i.object_id

查詢結果重要字段解釋:

database_id 表或視圖的數據庫 ID
TableName 表名稱
index_level

索引的當前位於B樹結構中的級別。

0 表示索引葉級別、堆以及 LOB_DATA 或 ROW_OVERFLOW_DATA 分配單元。

大於 0 的值表示非葉索引級別。 index_level 在索引的根級別中屬於最高級別。

僅當 mode = DETAILED 時才處理非葉級別的索引。

avg_fragmentation_in_percent

索引的邏輯碎片,或 IN_ROW_DATA 分配單元中堆的區碎片。

此值按百分比計算,並將考慮多個文件。

0 表示 LOB_DATA 和 ROW_OVERFLOW_DATA 分配單元。

如果是堆表且mode模式 為 Sampled 時,為 NULL。如果碎片小於10%~20%,碎片不太可能會成為問題,如果索引碎片在20%~40%,碎片可能成為問題,但是可以通過索引重組來消除索引解決,大規模的碎片(當碎片大於40%),可能要求索引重建。

page_count

索引或數據頁的總數。

對於索引,表示 IN_ROW_DATA 分配單元中 b 樹的當前級別中的索引頁總數。

對於堆,表示 IN_ROW_DATA 分配單元中的數據頁總數。

對於 LOB_DATA 或 ROW_OVERFLOW_DATA 分配單元,表示該分配單元中的總頁數。

record_count

總記錄數。

對於索引,記錄的總數應用於 IN_ROW_DATA 分配單元中 b 樹(包括非葉子數據頁的數量)的當前級別。

對於堆,表示 IN_ROW_DATA 分配單元中的總記錄數。

 注意

對於堆,此函數返回的記錄數可能與通過對堆運行 SELECT COUNT(*) 返回的行數不匹配。 這是因為一行可能包含多個記錄。 例如,在某些更新情況下,單個堆行可能由於更新操作而包含一條前推記錄和一條被前推記錄。 此外,多數大型 LOB 行在 LOB_DATA 存儲中拆分為多個記錄。

對於 LOB_DATA 或 ROW_OVERFLOW_DATA 分配單元,表示整個分配單元中總記錄數。

當 mode 為 LIMITED 時,為 NULL。

index_id

索引的索引 ID。

0 = 堆。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2、關於碎片的解決方法:

1.刪除索引並重建

  這種方式有如下缺點:

  索引不可用:在刪除索引期間,索引不可用。

  阻塞:卸載並重建索引會阻塞表上所有的其他請求,也可能被其他請求所阻塞。

  對於刪除聚集索引,則會導致對應的非聚集索引重建兩次(刪除時重建,建立時再重建,因為非聚集索引中有指向聚集索引的指針)。

  唯一性約束:用於定義主鍵或者唯一性約束的索引不能使用DROP INDEX語句刪除。而且,唯一性約束和主鍵都可能被外鍵約束引用。在主鍵卸載之前,所有引用該主鍵的外鍵必須首先被刪除。盡管可以這么做,但這是一種冒險而且費時的碎片整理方法。

  基於以上原因,不建議在生產數據庫,尤其是非空閑時間不建議采用這種技術。

  2.使用DROP_EXISTING語句重建索引

  為了避免重建兩次索引,使用DROP_EXISTING語句重建索引,因為這個語句是原子性的,不會導致非聚集索引重建兩次,但同樣的,這種方式也會造成阻塞。

CREATE UNIQUE CLUSTERED INDEX IX_C1 ON t1(c1)
WITH (DROP_EXISTING = ON)

  缺點:

  阻塞:與卸載重建方法類似,這種技術也導致並面臨來自其他訪問該表(或該表的索引)的查詢的阻塞問題。

  使用約束的索引:與卸載重建不同,具有DROP_EXISTING子句的CREATE INDEX語句可以用於重新創建使用約束的索引。如果該約束是一個主鍵或與外鍵相關的唯一性約束,在CREATE語句中不能包含UNIQUE。

  具有多個碎片化的索引的表:隨着表數據產生碎片,索引常常也碎片化。如果使用這種碎片整理技術,表上所有索引都必須單獨確認和重建。

  3.使用ALTER INDEX REBUILD語句重建索引

  使用這個語句同樣也是重建索引,但是通過動態重建索引而不需要卸載並重建索引.是優於前兩種方法的,但依舊會造成阻塞。可以通過ONLINE關鍵字減少鎖,但會造成重建時間加長。

  阻塞:這個依然有阻塞問題。

  事務回滾:ALTER INDEX REBUILD完全是一個原子操作,如果它在結束前停止,所有到那時為止進行的碎片整理操作都將丟失,可以通過ONLINE關鍵字減少鎖,但會造成重建時間加長。

  4.使用ALTER INDEX REORGANIZE

  這種方式不會重建索引,也不會生成新的頁,僅僅是整理葉級數據,不涉及非葉級,當遇到加鎖的頁時跳過,所以不會造成阻塞。但同時,整理效果會差於前三種。

  4種索引整理技術比較:

 

特性/問題 卸載並重建索引 DROP_EXISTING ALTER INDEX REBUILD ALTER INDEX REORGANIZE
在聚集索引碎片整理時,重建非聚集索引 兩次
丟失索引
整理具有約束的索引的碎片 高度復雜 復雜性適中 簡單 簡單
同時進行多個索引的碎片整理
並發性 中等,取決於冰法用戶活動
中途撤銷 因為不使用事務,存在危險 進程丟失 進程丟失 進程被保留
碎片整理程度 中到低
應用新的填充因子
更新統計


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM