如何解決DBCC SHRINKFILE命令不起作用
阿新 • • 發佈:2019-01-08
----為什麼DBCC SHRINKFILE會不起作用---- -->>TravyLee生成測試資料 if OBJECT_ID('testdb')is not null drop database testdb go create database testdb; go use testdb go if OBJECT_ID('test') is not null drop table test go create table test ( a int, b nvarchar(3900) ) go declare @i int set @i=1 while @i<=1000 begin insert into test VALUES( 1,REPLICATE(N'a',3900)) insert into test VALUES( 2,REPLICATE(N'b',3900)) insert into test VALUES( 3,REPLICATE(N'c',3900)) insert into test VALUES( 4,REPLICATE(N'd',3900)) insert into test VALUES( 5,REPLICATE(N'e',3900)) insert into test VALUES( 6,REPLICATE(N'f',3900)) insert into test VALUES( 7,REPLICATE(N'g',3900)) insert into test VALUES( 8,REPLICATE(N'h',3900)) set @
[email protected]+1 end --select * from test 使用DBCC SHOWCONTIG命令來檢視這個表的儲存資料 dbcc showcontig('test') --結果一 /* DBCC SHOWCONTIG 正在掃描 'test' 表... 表: 'test' (2121058592);索引 ID: 0,資料庫 ID: 9 已執行 TABLE 級別的掃描。 - 掃描頁數................................: 8000 - 掃描區數..............................: 1002 - 區切換次數..............................: 1001 - 每個區的平均頁數........................: 8.0 - 掃描密度 [最佳計數:實際計數].......: 99.80% [1000:1002] - 區掃描碎片 ..................: 0.20% - 每頁的平均可用位元組數.....................: 279.0 - 平均頁密度(滿).....................: 96.55% DBCC 執行完畢。如果 DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */ 從上述結果中可以看到這個表的資料的儲存申請了8000頁 現在刪除每個區裡面的7個頁面,只保留a=5的這些記錄 delete test where a<>5 go 使用系統儲存過程sp_spaceused 查看錶的空間資訊 sp_spaceused test go /* name rows reserved data index_size unused -------- ----------- -- ------------------------------------------- test 1000 64008 KB 32992 KB 8 KB 31008 KB */ 使用DBCC SHOWCONTIG命令檢視儲存情況 DBCC SHOWCONTIG(test) --結果二 /* DBCC SHOWCONTIG 正在掃描 'test' 表... 表: 'test' (2121058592);索引 ID: 0,資料庫 ID: 9 已執行 TABLE 級別的掃描。 - 掃描頁數................................: 4124 - 掃描區數..............................: 1002 - 區切換次數..............................: 1001 - 每個區的平均頁數........................: 4.1 - 掃描密度 [最佳計數:實際計數].......: 51.50% [516:1002] - 區掃描碎片 ..................: 0.20% - 每頁的平均可用位元組數.....................: 6199.0 - 平均頁密度(滿).....................: 23.41% DBCC 執行完畢。如果 DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */ 我們把結果一和結果二做一個比較 ------------------------------------------- 結果 掃描頁數 掃描區數 ------------------------------------------- 一 8000 1002 ------------------------------------------- 二 4124 1002 ------------------------------------------- 通過上面的表的資料的對比我們容易發現還有將近一半的頁面沒有被釋放 這時我們來對我們去對檔案進行收縮: DBCC SHRINKFILE(1,40) /* DbId FileId CurrentSize MinimumSize UsedPages EstimatedPages ------------------------------------------------------------------------------ 9 1 8168 288 1160 1160 */ 通過這個結果,我們來計算一下資料檔案中正在被使用的大小 --(8168*8.0)/1024=63.812500M --正好是1000個區大小 這種情況就證明了我們收縮資料庫的DBCC SHRINKFILE(1,40) 指令並沒有起到應有的作用 那麼我們如何解決這個問題呢? 如果這個標有聚集索引,我們可以通過重建索引把頁面從排一次, 但這個表沒有聚集索引 接下來我建立聚集索引: create clustered index test_a_idx on test(a) go --使用DBCC SHOWCONTIG(test)命令查看錶的儲存情況 DBCC SHOWCONTIG(test) /* DBCC SHOWCONTIG 正在掃描 'test' 表... 表: 'test' (2121058592);索引 ID: 1,資料庫 ID: 9 已執行 TABLE 級別的掃描。 - 掃描頁數................................: 1000 - 掃描區數..............................: 125 - 區切換次數..............................: 124 - 每個區的平均頁數........................: 8.0 - 掃描密度 [最佳計數:實際計數].......: 100.00% [125:125] - 邏輯掃描碎片 ..................: 0.00% - 區掃描碎片 ..................: 0.00% - 每頁的平均可用位元組數.....................: 273.0 - 平均頁密度(滿).....................: 96.63% DBCC 執行完畢。如果 DBCC 輸出了錯誤資訊,請與系統管理員聯絡。 */ 通過上述結果可以發現,建立聚集索引之後,原先存放在 堆裡的資料以B樹的方式從新存放。 原先的頁面被釋放出來了,佔用的分割槽也被釋放出來了。 這個時候再使用DBCC SHRINKFILE就有效果了 DBCC SHRINKFILE(1,40) /* DbId FileId CurrentSize MinimumSize UsedPages EstimatedPages ---------------------------------------------------------------------------------- 9 1 5120 288 1168 1168 */ 以上現象是因為資料儲存頁面分散在區裡,造成了SHRINKFILE效果不佳。 在一個有聚集索引的表上,這個問題可以通過重建索引來解決。 如果這些去裡面放的是text或者image型別的資料, SQL Server會用單獨的頁面來儲存這些資料。 如果儲存這一類頁面的區發生了這樣的問題,和堆一樣 做索引重建也不會影響到他們。簡單的方法就是把這些可能有問題的物件 都找出來,然後重建他們。可以使用DBCC EXTENTINFO這個命令開啟資料 檔案裡區的分配資訊。然後計算每個物件理論上的區的數目和實際的數目, 如果實際數目遠遠大於理論數目,那這個物件就是碎片過多, 可以考慮重建物件 --還是以剛才的資料為例演示如何找出這些需要重建的物件 drop table test go if OBJECT_ID('test') is not null drop table test go create table test ( a int, b nvarchar(3900) ) go declare @i int set @i=1 while @i<=1000 begin insert into test VALUES( 1,REPLICATE(N'a',3900)) insert into test VALUES( 2,REPLICATE(N'b',3900)) insert into test VALUES( 3,REPLICATE(N'c',3900)) insert into test VALUES( 4,REPLICATE(N'd',3900)) insert into test VALUES( 5,REPLICATE(N'e',3900)) insert into test VALUES( 6,REPLICATE(N'f',3900)) insert into test VALUES( 7,REPLICATE(N'g',3900)) insert into test VALUES( 8,REPLICATE(N'h',3900)) set @[email protected]+1 end go delete from test where a<>5 go --建立表extentinfo用來存放分割槽資訊 if OBJECT_ID('extentinfo') is not null drop table extentinfo go create table extentinfo ( file_id smallint, page_id int, pg_alloc int, ext_size int, obj_id int, index_id int, partition_number int, partition_id bigint, iam_chain_type varchar(50), pfs_bytes varbinary(10) ) go create proc inport_extentinfo as dbcc extentinfo('testdb') go insert extentinfo exec inport_extentinfo go select FILE_ID, obj_id, index_id, partition_id, ext_size, 'actual_extent_count'=COUNT(*), 'actual_page_count'=SUM(pg_alloc), 'possible_extent_count'=CEILING(SUM(pg_alloc)*1.0/ext_size), 'possible_extents/actual_extents'=(CEILING(SUM(pg_alloc)*1.00/ext_size)*100.00)/COUNT(*) from extentinfo group by FILE_ID, obj_id, index_id, partition_id, ext_size having COUNT(*)-CEILING(SUM(pg_alloc)*1.0/ext_size)>0 order by partition_id, obj_id, index_id, FILE_ID /* FILE_ID obj_id index_id partition_id ext_size actual_extent_count actual_page_count possible_extent_count possible_extents/actual_extents ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 1 2137058649 0 72057594038976512 8 998 4115 515 51.603206412 */ select object_name(2137058649) as TName /* TName ---------------- test */ --此時我們可以找到這個存在未被釋空間放的表, --這時我們就需要對這些物件進行重建處理