首先要申明,一般情形下沒有必要對用戶數據庫的數據文件進行收縮,因為雖然可能看到有很多空間被占用,但是實際未釋放,但是當數據庫中有新的對象或者新的數據進來時,這些空間是會被重新使用到的。
但是在某些特定情況下,比如磁盤空間快滿了,但是硬件小組暫時並未有足夠的資源可以調配,如果發現了該磁盤的某個數據庫中有大量的未使用空間,可能心想“可以搞定”。但是當實際收縮的時候,一直無法收縮,又涼涼了。
那么這個時候,我們就要看下,為什么實際存在未使用空間,但是實際卻無法收縮。
DBCC SHRINKFILE的工作原理:
DBCC SHRINKFILE執行時,是做的區(Extent)級別的動作。他會將使用過的區前移,沒有使用的區從文件中移除掉。但是,並不會將一個區中的空頁移除,然后合並區,也不會把頁面中的空間移除,合並頁面。所以如果一個數據庫中有很多的區,但是這些區中只有一兩個頁才有數據,那么DBCC SHRINKFILE就不會起作用。 |
這也就解釋了上面所說的那些情況,為什么明明看到數據文件有空間,但是不能壓縮或者清空。通常的原因是數據文件里雖然有很多空的頁面,但是頁面是分布在各個區中,所以就沒辦法壓縮了。
舉個例子:
use wisontest go create table show_extent (a int, b nvarchar(3900)) go declare @i int set @i=1 while @i<=1000 begin insert into show_extent values (1,replicate(N'a',3900)) insert into show_extent values (2,replicate(N'b',3900)) insert into show_extent values (3,replicate(N'c',3900)) insert into show_extent values (4,replicate(N'd',3900)) insert into show_extent values (5,replicate(N'e',3900)) insert into show_extent values (6,replicate(N'f',3900)) insert into show_extent values (7,replicate(N'g',3900)) insert into show_extent values (8,replicate(N'h',3900)) set @i=@i+1 end dbcc showcontig('show_extent')
這個時候得到如下的結果。可以看到申請了8000個數據頁,也就是1001個區。
DBCC SHOWCONTIG scanning 'show_extent' table... |
此時,我們刪除掉每個分區中的7個頁面,只保留a=5的記錄
delete show_extent where a<>5 exec sp_spaceused show_extent exec sp_spaceused
返回的結果如下,可以看到還是有一半的空間被占用到了(雖然實際上這個時候應該只使用1/8的空間才對)。
如果此時,我們去執行DBCC SHRINKFILE,那么我們會發現沒有什么效果。面對這種情況的時候,我們可以通過對該表新建一個聚集索引來使得可以收縮文件(如果表有聚集索引,那么就是重建聚集索引)。
create clustered index icx_show_extent on show_extent(a) exec sp_spaceused show_extent exec sp_spaceused
此時就可看到未使用空間和理論值一樣了,如果此時需要做收縮文件的操作,那么就可以達到效果了。