本文分為兩個問題: 第一,碎片是什么;第二,碎片怎么處理;
現在,來找解決這兩個問題:
一、碎片是什么
說到碎片,就要提到索引了,索引用着挺爽的啊!是的,一旦索引建立,我們搜索數據的效率就提高了;然后我們就要想一想了,索引將我們的數據排序了,不管聚集還是非聚集索引總歸是將數據排序了。這些數據給排序了,那么問題來了,我們個這些已經排序的數據進行插入操作會產生什么后果?人家本來好好排着隊呢!插隊的來了!
假設一群很瘦的人在排隊,突然插入一個胖子,且不說胖子搗亂了正常的排序規則,胖子插進隊伍中,一個人占了兩個人的空間,這個時候對於瘦的人來說,胖子浪費了一個人的空間,如果不斷的有胖子插隊,浪費的空間會越來越多。浪費的空間在數據庫中就是碎片!這里有一篇博客,舉例說明了在數據庫中碎片的產生。在數據庫中,數據存在於頁內,一個頁是8K,索引已經建立的前提下,這8K數據是好好的排序待着的,然后,向這個頁中插入一條數據(比原來數據中最大行還要大),這時后面數據就會遵循B樹規則開始移動,因為這個頁已經滿了,后面的數據就會被擠出去若干條到下一頁,如果下一頁也是滿的,依次往下排,這對於數據庫來說消耗是很大的。這個過程中,在最初的頁中,就會產生碎片,因為插入的數據偏大,導致后面的數據后移到別人頁,就會空出空間了,而這部分空間就暫時沒法利用,造成碎片。這種情況屬於內部碎片,是頁內的。因為在這個區中可能,頁與頁之間還是連續的。僅僅是最初的那個頁中的數據是斷續的。首先,插入數據造成數據移動消耗大,另外,在頁中打亂了頁內的排序,造成查詢性能下降。這就是碎片造成影響。
另一種,情況就是插入的數據使頁與頁之間造成斷續,比如,插入的數據正好在頁中最后一行,被擠出到別的頁的數據,與原來的頁之間沒有了連續,這后果就更嚴重了,這種情況就是外部的碎片。
這樣看,在一定程度上索引的性能就下降了。就需要重新維護索引了。怎么維護呢?將原來的索引刪除,重新構建?這種做法,在某些場景中是很合適的。比如,插入的動作比較少的情況下,在合適的時候,重現構建索引是可取的。除了,重構呢?
二、碎片怎么處理
我們要進行索引的維護,首先要知道一些查詢數據庫性能的語句,來獲取當前數據庫的性能,做出判斷之后,采取不同的措施。
此時我們用到的就是元數據函數sys.dm_db_index_physical_stats,它有助於確定數據庫中的頁和區段有多滿。使用這個函數得到當前數據的一些性能信息。圖片來源於參考博客。
下面,先來說第一種類似於重建:
1、使用DROP_EXISTING語句重建索引:為了避免重建兩次索引,使用DROP_EXISTING語句重建索引,因為這個語句是原子性的,不會導致非聚集索引重建兩次,但同樣的,這種方式也會造成阻塞。缺點:阻塞:與卸載重建方法類似,這種技術也導致並面臨來自其他訪問該表(或該表的索引)的查詢的阻塞問題。、
使用約束的索引:與卸載重建不同,具有DROP_EXISTING子句的CREATE INDEX語句可以用於重新創建使用約束的索引。如果該約束是一個主鍵或與外鍵相關的唯一性約束,在CREATE語句中不能包含UNIQUE。
具有多個碎片化的索引的表:隨着表數據產生碎片,索引常常也碎片化。如果使用這種碎片整理技術,表上所有索引都必須單獨確認和重建。
CREATE UNIQUE CLUSTERED INDEX IX_C1 ON t1(c1)
WITH (DROP_EXISTING = ON)
2、用ALTER INDEX REBUILD語句重建索引:使用這個語句同樣也是重建索引,但是通過動態重建索引而不需要卸載並重建索引.是優於前兩種方法的,但依舊會造成阻塞。可以通過ONLINE關鍵字減少鎖,但會造成重建時間加長。
阻塞:這個依然有阻塞問題。
事務回滾:ALTER INDEX REBUILD完全是一個原子操作,如果它在結束前停止,所有到那時為止進行的碎片整理操作都將丟失,可以通過ONLINE關鍵字減少鎖,但會造成重建時間加長。
3、使用ALTER INDEX REORGANIZE:這種方式不會重建索引,也不會生成新的頁,僅僅是整理葉級數據,不涉及非葉級,當遇到加鎖的頁時跳過,所以不會造成阻塞。但同時,整理效果會差於前三種。
4、填充因子:填充因子的概念可以理解為預留一定的空間存放插入和更新新增加的數據,以避免頁拆分。 填充因子值的選擇:
如何設置填充因子的值並沒有一個公式或者理念可以准確的設置。使用填充因子雖然可以減少更新或者插入時的分頁,但同時因為需要更多的頁,所以降低了查詢的性能和占用更多的磁盤空間.如何設置這個值進行trade-off需要根據具體的情況來看。具體情況要根據對於表的讀寫比例來看,我這里給出我認為比較合適的值:
- 當讀寫比例大於100:1時,不要設置填充因子,100%填充
- 當寫的次數大於讀的次數時,設置50%-70%填充
- 當讀寫比例位於兩者之間時80%-90%填充
本文參考: SQL Server索引的維護 - 索引碎片、填充因子 <第三篇>