end -に最も近いオブジェクトをefficiently識別する方法を探していますSQL Serverデータファイル。このアプローチは、大きなデータファイルに対してもパフォーマンスを維持する必要があります。
次のクエリは、SQL 2012に同梱されている、文書化されていない動的管理関数を利用しています: sys.dm_db_database_page_allocations
;このDMFは DBCC IND
コマンド。
次のクエリは、特定のデータファイルの最後のオブジェクトを識別します(警告:ある時点でキャンセルしない限り、25 GBを超えるデータベースに対してこれを実行しないでください/):
-- Return object with highest Extent Page ID
SELECT files.name as logical_file_name
, files.physical_name as physical_file_name
, OBJECT_SCHEMA_NAME(object_id) + N'.' + OBJECT_NAME(object_id) AS object_name
, alloc.*
FROM sys.dm_db_database_page_allocations(DB_ID(), NULL, NULL, NULL, NULL) alloc
INNER JOIN sys.database_files files
ON alloc.extent_file_id = files.file_id
WHERE is_allocated = 1
AND files.name = 'Logical_FileName'
ORDER BY files.name , files.physical_name, extent_page_id DESC
上記のWarningが示すように、この関数は実際には特定のオブジェクトではなく特定のオブジェクトを参照するための先の尖ったアプローチ用に設計されているため、データベースのサイズが大きくなるとこのクエリの実行速度が遅くなります問題の特定のデータファイルを確認します。私がしたようにNULL
パラメータを渡すと、この関数はデータベース内のすべてのオブジェクトをバックグラウンドで反復処理し、結合された出力を吐き出します。これは私が必要とすることを実現しますが、最適化に役立たない非常にbrute-forceの方法で実現します。
GAM、SGAM や IAMチェーン を繰り返し処理して、特定のデータファイルの最後にあるオブジェクトをすばやく特定する方法があることを願っています。最後のオブジェクトの目的を見つけるためにページ割り当てマップをトラバースする DBCC PAGE 呼び出し、またはそのような性質を使用して、TSQLの外にこのアプローチをプッシュしてPowerShellのようなものにプッシュする必要があると思います特定のデータファイルです。 ...そして、誰かがそのコードをすでに一緒にスローしたり、これらの構造やドキュメント化されていないプロシージャの出力を私よりもよく理解していることを願っています。
これは多くの人が尋ねる避けられない質問なので、すぐに答えます。私が取り組んでいるプロジェクトは、レガシーシステムを高速化することですが、 ヒープテーブルの束 を統合した後(他の理由で悪夢でした)、今はデータファイル内の空き領域の束。このスペースを解放してOSに戻したいのですが、 オブジェクトを別のデータファイルに移行する従来のアプローチ は、操作するのに十分な空きスペースがないため、この段階では実行できませんシステム上(このデータファイルからより多くの領域を解放できるまで)。
ファイルの拡張を無効にして DBCC SHRINKFILE TRUNCATEONLY
コマンドファイルを毎晩開いて、データファイルの終わりに向かって開いているページを解放しますが、これはmayと同じくらい頻繁に機能する、時間のかかる困難なプロセスですしません。ファイルの終わりに近いオブジェクトが何であるかを識別して、手動で再構築し、より速いタイムテーブルでスペースを解放できるようにしたいと思っています。
quickly特定のデータファイルの最後にあるオブジェクトの名前を特定する方法はありますか?私が現在採用している方法は私のニーズを満たしていないため、利用可能なあらゆるアプローチを使用することにオープンです。
これでうまくいくと思います。このコードは基本的に次のことを行います。
DBCC SHRINKFILE
コマンドを提供し(即時に実行する必要があります)、実質的にDBCC SHRINKFILE
と同等です TRUNCATEONLY
これは、データベース内のデータファイルのページIDを反復処理するカーソルにネストされ、私のローカライズされたテストに基づいて非常に迅速に実行されます。 IAMやPFSページなど、テーブルやインデックスで予約されていないページがデータファイルの終わりを占めているかどうかを識別する機能も追加しました。
SET NOCOUNT ON;
-- Create Temp Table to Push DBCC PAGE results into
CREATE TABLE #dbccPage_output(
ID INT IDENTITY(1,1)
, [ParentObject] VARCHAR(255)
, [Object] VARCHAR(255)
, [Field] VARCHAR(255)
, [Value] VARCHAR(255)
)
GO
-- Variables to hold pointer information for traversing GAM and SGAM pages
DECLARE @GAM_maxPageID INT, @SGAM_maxPageID INT, @maxPageID INT,
@GAM_page INT, @SGAM_page INT
DECLARE @stmt VARCHAR(2000)
-- Final Output Table
DECLARE @myOutputTable TABLE
(
LogicalFileName VARCHAR(255)
, ObjectID BIGINT
, IndexID BIGINT
, PartitionID BIGINT
, MaxPageID BIGINT
)
-- Cursor to iterate through each file
DECLARE cursorFileIds CURSOR
FOR
SELECT file_id, size
FROM sys.database_files
WHERE type = 0
-- Variable to hold fileID
DECLARE @fileID INT, @size INT, @interval INT
-- Inject the data into the cursor
OPEN cursorFileIds
FETCH NEXT FROM cursorFileIds
INTO @fileID, @size
-- Enter the While Loop. This loop will end when the
-- end of the data injected into the cursor is reached.
WHILE @@FETCH_STATUS = 0
BEGIN
-- Truncate table (mainly used for 2nd pass and forward)
TRUNCATE TABLE #dbccPage_output
-- Referenced if we need to step back a GAM/SGAM interval
STEPBACK:
-- # of pages in a GAM interval
SET @interval = @size / 511232
-- Set GAM Page to read
SET @GAM_page = CASE @interval WHEN 0 THEN 2 ELSE @interval * 511232 END
-- Set SGAM page to read (always the next page after the GAM)
SET @SGAM_page = CASE @interval WHEN 0 THEN 3 ELSE (@interval * 511232) + 1 END
-- Search Last GAM Interval page
SET @stmt = 'DBCC PAGE(0, ' + CAST(@fileID AS VARCHAR(10)) + ', ' + CAST(@GAM_page AS VARCHAR(20)) + ', 3) WITH TABLERESULTS, NO_INFOMSGS' -- GAM on Primary Datafile
PRINT @stmt
INSERT INTO #dbccPage_output ([ParentObject], [Object], [Field], [Value])
EXEC (@stmt)
-- Get Last Allocated Page Number
SELECT TOP 1
@GAM_maxPageID = REVERSE(SUBSTRING(REVERSE(Field), CHARINDEX(')', REVERSE(Field)) + 1, CHARINDEX(':', REVERSE(Field)) - CHARINDEX(')', REVERSE(Field)) - 1))
FROM #dbccPage_output
WHERE [Value] = ' ALLOCATED'
ORDER BY ID DESC
-- Truncate Table
TRUNCATE TABLE #dbccPage_output
-- Search Last SGAM Interval page
SET @stmt = 'DBCC PAGE(0, ' + CAST(@fileID AS VARCHAR(10)) + ', ' + CAST(@SGAM_page AS VARCHAR(20)) + ', 3) WITH TABLERESULTS, NO_INFOMSGS' -- SGAM on Primary Datafile
PRINT @stmt
INSERT INTO #dbccPage_output ([ParentObject], [Object], [Field], [Value])
EXEC (@stmt)
-- Get Last Allocated Page Number
SELECT TOP 1
@SGAM_maxPageID = REVERSE(SUBSTRING(REVERSE(Field), CHARINDEX(')', REVERSE(Field)) + 1, CHARINDEX(':', REVERSE(Field)) - CHARINDEX(')', REVERSE(Field)) - 1))
FROM #dbccPage_output
WHERE [Value] = ' ALLOCATED'
ORDER BY ID DESC
-- Get highest page value between SGAM and GAM
SELECT @maxPageID = MAX(t.value)
FROM (VALUES (@GAM_maxPageID), (@SGAM_maxPageID)) t(value)
TRUNCATE TABLE #dbccPage_output
-- Check if GAM or SGAM is last allocated page in the chain, if so, step back one interval
IF(@maxPageID IN (@GAM_page, @SGAM_page))
BEGIN
SET @size = ABS(@size - 511232)
GOTO STEPBACK
END
-- Search Highest Page Number of Data File
SET @stmt = 'DBCC PAGE(0, ' + CAST(@fileID AS VARCHAR(10)) + ', ' + CAST(CASE WHEN @maxPageID = @SGAM_maxPageID THEN @maxPageID + 7 ELSE @maxPageID END AS VARCHAR(50)) + ', 1) WITH TABLERESULTS, NO_INFOMSGS' -- Page ID of Last Allocated Object
PRINT @stmt
INSERT INTO #dbccPage_output ([ParentObject], [Object], [Field], [Value])
EXEC (@stmt)
-- Capture Object Name of DataFile
INSERT INTO @myOutputTable
SELECT (SELECT name FROM sys.database_files WHERE file_id = @fileID) AS LogicalFileName
, CASE WHEN (SELECT [Value] FROM #dbccPage_output WHERE Field = 'm_type') IN ('1', '2') THEN -- If page type is data or index
CAST((SELECT [Value] FROM #dbccPage_output WHERE Field = 'Metadata: ObjectId') AS BIGINT)
ELSE CAST((SELECT [Value] FROM #dbccPage_output WHERE Field = 'm_type') AS BIGINT)
END AS ObjectID
, NULLIF(CAST((SELECT [Value] FROM #dbccPage_output WHERE Field = 'Metadata: IndexId') AS BIGINT), -1) AS IndexID
, NULLIF(CAST((SELECT [Value] FROM #dbccPage_output WHERE Field = 'Metadata: PartitionId') AS BIGINT), 0) AS PartitionID
, @maxPageID + 7 AS MaxPageID
-- Reset Max Page Values
SELECT @GAM_maxPageID = 0, @SGAM_maxPageID = 0, @maxPageID = 0
-- Traverse the Data in the cursor
FETCH NEXT FROM cursorFileIds
INTO @fileID, @size
END
-- Close and deallocate the cursor because you've finished traversing all it's data
CLOSE cursorFileIds
DEALLOCATE cursorFileIds
-- page type values pt. 1: https://www.sqlskills.com/blogs/paul/inside-the-storage-engine-using-dbcc-page-and-dbcc-ind-to-find-out-if-page-splits-ever-roll-back/
-- page type values pt. 2: https://www.sqlskills.com/blogs/paul/inside-the-storage-engine-anatomy-of-a-page/
-- ObjectIDs of either 0 or 99: https://www.sqlskills.com/blogs/paul/finding-table-name-page-id/
-- Output Object Closest to the End
SELECT t.LogicalFileName
, CAST(CASE WHEN t.IndexID IS NULL THEN
CASE t.ObjectID
WHEN 0 THEN '>>No MetaData Found<<' -- This isn't m_type, rather ObjectID
WHEN 1 THEN '>>Data Page<<'
WHEN 2 THEN '>>Index Page<<'
WHEN 3 THEN '>>Text Mix Page<<'
WHEN 4 THEN '>>Text Tree Page<<'
WHEN 6 THEN '>>DCM Page<<<'
WHEN 7 THEN '>>Sort Page<<'
WHEN 8 THEN '>>GAM Page<<'
WHEN 9 THEN '>>SGAM Page<<'
WHEN 10 THEN '>>IAM Page<<'
WHEN 11 THEN '>>PFS Page<<'
WHEN 13 THEN '>>Boot Page<<'
WHEN 15 THEN '>>File Header Page<<'
WHEN 16 THEN '>>Diff Map Page<<'
WHEN 17 THEN '>>ML Map Page<<'
WHEN 18 THEN '>>Deallocated DBCC CHECKDB Repair Page<<'
WHEN 19 THEN '>>Temporary ALTER INDEX Page<<'
WHEN 20 THEN '>>Pre-Allocated BULK LOAD Page<<'
WHEN 99 THEN '>>Possible Page Corruption/Run DBCC CHECKDB<<' -- This isn't m_type, rather ObjectID
ELSE CAST(t.ObjectID AS VARCHAR(50))
END
ELSE QUOTENAME(OBJECT_SCHEMA_NAME(t.ObjectID)) + '.' + QUOTENAME(OBJECT_NAME(t.ObjectID)) END AS VARCHAR(250)) AS TableName
, QUOTENAME(i.name) AS IndexName
, p.partition_number AS PartitionNumber
, 'DBCC SHRINKFILE(' + t.LogicalFileName + ', ' + CAST(CEILING((t.MaxPageID + 8) * 0.0078125) AS VARCHAR(50)) + ')' AS ShrinkCommand_Explicit
, 'DBCC SHRINKFILE(' + t.LogicalFileName + ', TRUNCATEONLY)' AS ShrinkCommand_TRUNCATEONLY
FROM @myOutputTable t
LEFT JOIN sys.indexes i
ON t.ObjectID = i.object_id
AND t.IndexID = i.index_id
LEFT JOIN sys.partitions p
ON t.ObjectID = p.object_id
AND t.PartitionID = p.partition_id
-- Cleanup
DROP TABLE #dbccPage_output
GO
次のコードは、最大のページ番号から最小のページ番号まで、各データベースページをチェックして、割り当てられているかどうかを確認します。最初に割り当てられたページが見つかると、そのページに関連付けられているオブジェクトが表示されます。最後に割り当てられたページが実際のオブジェクトを参照していない可能性があるため、動作が保証されていません。ただし、動作するはずですほとんどの場合。
SET NOCOUNT ON;
IF OBJECT_ID(N'tempdb..#dbcrep', N'U') IS NOT NULL
DROP TABLE #dbcrep;
CREATE TABLE #dbcrep
(
ParentObject VARCHAR(128)
, [Object] VARCHAR(128)
, [Field] VARCHAR(128)
, VALUE VARCHAR(2000)
);
DECLARE @cmd nvarchar(max);
DECLARE @PageNum int;
DECLARE @PageCount int;
DECLARE @FileID int;
DECLARE @Status varchar(2000);
SET @FileID = 1;
SET @PageCount = (
SELECT df.size
FROM sys.database_files df
WHERE df.file_id = @FileID
);
SET @PageNum = @PageCount - 1;
WHILE @PageNum > 0
BEGIN
SET @cmd = N'DBCC PAGE (''' + DB_NAME() + N''', ' + CONVERT(nvarchar(20), @FileID) + N', ' + CONVERT(nvarchar(20), @PageNum) + N', 0) WITH TABLERESULTS, NO_INFOMSGS;';
DELETE FROM #dbcrep;
INSERT INTO #dbcrep (ParentObject, [Object], [Field], [VALUE])
EXEC sys.sp_executesql @cmd;
SELECT @Status = VALUE
FROM #dbcrep
WHERE ParentObject = 'PAGE HEADER:'
AND Object = 'Allocation Status'
AND Field LIKE 'GAM %';
SET @PageNum -= 1;
PRINT @Status;
IF @Status <> 'NOT ALLOCATED' BREAK
END
SELECT ObjectName = s.name + N'.' + o.name
, d.*
FROM #dbcrep d
LEFT JOIN sys.all_objects o ON d.VALUE = o.object_id
LEFT JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE ParentObject = 'PAGE HEADER:'
AND Object = 'Page @0x00000001BA28E000'
AND Field = 'Metadata: ObjectId';
現在のデータベースで指定されたfile_idに割り当てられたページ数を取得し、ループを使用してDBCC PAGE
で各ページを検査し、その出力を一時テーブルに保存します。次に、一時テーブルをsys.all_objects
に結合して、ページが割り当てられているオブジェクトの名前を取得します。
私のテストリグでは、次の結果が表示されます。
╔════════════════════╦══════════════╦═════════════ ═════════════╦════════════════════╦════════════╗ ║ObjectName║ParentObject║Object║Field║VALUE║ ╠════════════════════╬═════════ ═════╬══════════════════════════╬═════════════════ ═══╬════════════╣ ║dbo.EmptyDatabases║ページヘッダー:║ページ@ 0x00000001BA28E000║メタデータ:ObjectId║1938105945║ ╚═══ ═════════════════╩══════════════╩═════════════════ ═════════╩════════════════════╩════════════╝
#dbcrep
tempテーブルには、次の詳細が含まれています。
╔══════════════╦══════════════════════════╦═══════ ════════════════════════╦════════════════════╗ ║ParentObject║Object║Field║VALUE║ ╠══════════════╬═══════════════════ ═══════╬═══════════════════════════════╬══════════ ══════════╣ ║バッファ:║BUF @ 0x0000000200E95B80║bページ║0x00000001BA28E000║ ║バッファ::BUF @ 0x0000000200E95B80║bhash║0x0000000000000000║ ║バッファ:║BUF @ 0x0000000200E95B80║bpageno║(1:42743)║ ║バッファ:║BUF @ 0x0000000200E95B80║bdbid║7║ ║バッファ:║BUF @ 0x0000000200E95B80║breference ║バッファ:║BUF @ 0x0000000200E95B80║bcputicks║0║ ║バッファ:║BUF @ 0x0000000200E95B80║bsampleCount ║0║ ║バッファ:║BUF @ 0x0000000200E95B80║bUse1║10982║ ║バッファ:BUF BUF @ 0x0000000200E95B80║bstat║0x9║ ║バッファ:║BUF @ 0Bx00000000ブログ║0x2121215a║ ║バッファ:║BUF @ 0x0000000200E95B80║bnext║0x0000000000000000║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║m_pageId║(1:42743)║[.____。ヘッダー:║ページ@ 0x00000001BA28E000║m_headerVersion║1║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║m_type║20║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║m_typeFlagBits║m_typeFlagBits 。]║ページヘッダー::ページ@ 0x00000001BA28E000║m_level║0║ ║ページヘッダー:║ページ@ 0 x00000001BA28E000║m_flagBits║0x204║[.____。 256║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║メタデータ:AllocUnitId║72057594052804608║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║メタデータ:PartitionId║72057594043301888║ :║ページ@ 0x00000001BA28E000║メタデータ:IndexId║1║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║メタデータ:ObjectId║1938105945║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║m_prev :0)║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║m_nextPage║(0:0)║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║pminlen║8 ║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║m_slotCnt║0║ ║ページヘッダー:║ページ@ 0x00000001BA28E000║m_freeCnt║8096║ ║PAGE HEADER:║ページ@ E000 ║m_freeData║96║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║m_reservedCnt║0║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║m_lsn║(321:6718:151)║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║m_xactReserved║0║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║m_xdesId║(0:0)║ ║PAGE HEADER:║ページ@ 0x00000001 ║m_ghostRecCnt║0║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║m_tornBits║1253867700║ ║PAGE HEADER:║ページ@ 0x00000001BA28E000║DB F rag ID║1║ ║PAGE HEADER:║割り当てステータス║GAM(1:2)║ALLOCATED║ ║PAGE HEADER:║割り当てステータス║SGAM(1:3)║割り当てられていない║ ║ページヘッダー:║割り当てステータス║PFS(1:40440)║0x0 0_PCT_FULL║ ║ページヘッダー:║割り当てステータス║DIFF(1:6)║変更なし║ ║ページヘッダー:║割り当てステータス║ML(1:7)║NOT MIN_LOGGED║ ╚══════════════╩══════════ ════════════════╩═══════════════════════════════╩═ ═══════════════════╝