現象:資料檔 600MB,記錄檔實際使用 33MB,但檔案大小 2.8 GB。**`DBCC SHRINKFILE` / `BACKUP LOG WITH TRUNCATE_ONLY` 皆無效**,因 VLF 狀態為「作用中」無法釋放。
SET NOCOUNT ON;
DECLARE
@LogicalFileName SYSNAME = N'Marias_log', -- 邏輯檔名,從 sys.database_files 查詢
@MaxMinutes INT = 10, -- 最大嘗試分鐘數
@NewSizeMB INT = 100; -- 目標大小 (MB)
DECLARE @OriginalSize INT;
SELECT @OriginalSize = size
FROM sys.database_files
WHERE name = @LogicalFileName;
PRINT 'Original Size = ' + CAST(@OriginalSize * 8 / 1024 AS VARCHAR(10)) + ' MB';
CREATE TABLE #DummyTrans (DummyColumn CHAR(8000) NOT NULL);
DECLARE @Counter INT, @StartTime DATETIME, @TruncLog NVARCHAR(MAX);
SELECT @StartTime = GETDATE(),
@TruncLog = N'BACKUP LOG ' + QUOTENAME(DB_NAME()) + N' WITH NO_LOG'; -- 2008 以前用 TRUNCATE_ONLY,新版建議改完整/差異備份後再 SHRINK
-- 先嘗試直接縮小
DBCC SHRINKFILE (@LogicalFileName, @NewSizeMB);
EXEC (@TruncLog);
-- 迴圈寫入/刪除虛擬資料,推進 VLF 循環,將作用中 VLF 從尾端趕走
WHILE @MaxMinutes > DATEDIFF(MI, @StartTime, GETDATE())
AND @OriginalSize = (SELECT size FROM sys.database_files WHERE name = @LogicalFileName)
AND (@OriginalSize * 8 / 1024) > @NewSizeMB
BEGIN
SELECT @Counter = 0;
WHILE (@Counter < @OriginalSize / 16) AND (@Counter < 50000)
BEGIN
INSERT #DummyTrans VALUES ('Fill Log');
DELETE #DummyTrans;
SELECT @Counter = @Counter + 1;
END
EXEC (@TruncLog);
END
PRINT 'Final Size = ' + CAST((SELECT size FROM sys.database_files WHERE name = @LogicalFileName) * 8 / 1024 AS VARCHAR(10)) + ' MB';
DROP TABLE #DummyTrans;
SET NOCOUNT OFF;
| 禁止操作 | 後果 | 正確替代 |
|---|---|---|
| 直接在生產跑上述腳本 | 產生大量記錄 I/O、膨脹 tempdb、阻塞正常事務、打破備份鏈 | **先做完整備份 → 再做記錄備份 → 再 SHRINK** |
| 使用 `BACKUP LOG ... WITH TRUNCATE_ONLY / NO_LOG` | **破壞備份鏈**,導致無法時間點還原,2008 後已移除 | 正規 `BACKUP LOG TO DISK = '...'` |
| 設定 `RECOVERY SIMPLE` 再 SHRINK 再改回 `FULL` | 同樣破壞備份鏈,且若期間發生災難無法時間點恢復 | 維持 `FULL`,定期記錄備份自動截斷 VLF |
**記錄檔暴漲 = 備份策略缺失 或 長事務/同步延遲**。強制縮小只是急救,**定期記錄備份 + VLF 監控 + 合理自動成長** 才是長效治本。生產環境嚴禁破壞備份鏈的捷徑操作。