一,找到每個數據庫的日志文件大小
SQL Server:查看SQL日志文件大小命令:dbcc sqlperf(logspace)
DBA 日常管理工作中,很重要一項工作就是監視數據庫文件大小,及日志文件大小。如果你管理數據庫的有很多的話,每天一個一個數據庫的去查看文件大小就太費神了,那就寫個SQL腳本吧,放到 SQL Agent 中,每天自動去查看各個數據庫文件及日志文件的大小,然后再通過數據庫郵件,Email 到我們手中,豈不快哉!當然,可以把每天的記錄存放到數據庫中去,這樣數據庫及日志文件的增長趨勢,我們也就一目了然了。
這里,介紹下獲取數據庫日志文件大小的方法。其實很簡單,就是執行 SQL Server DBCC 命令:dbcc sqlperf(logspace)
dbcc sqlperf(logspace) 可以獲取實例中每個數據庫日志文件大小,及使用情況。如果要保存SQL日志文件大小,則需要先創建一個數據表,然后動態執行dbcc sqlperf(logspace)命令:
--創建數據庫日志文件信息表 CREATE TABLE [Control].[DatabaseLogSize]( [dbname] [nvarchar](100) NULL, [dbid] [int] NULL, [log_logical_name] [nvarchar](100) NULL, [logsize] [decimal](30, 2) NULL, [logused] [decimal](30, 2) NULL, [status] [int] NULL ) ON [PRIMARY] GO --借助dbcc sqlperf(logspace)將數據插入上面新建的表 INSERT INTO [Control].[DatabaseLogSize]([dbname],[logsize],[logused],[status]) EXECUTE('dbcc sqlperf(logspace)')
之后我們在表[DatabaseLogSize]中就有了 [dbname]、[logsize]、[logused]、[status]四列數據了
二、將每個數據庫的日志文件邏輯名找出來
因為后面我們要使用DBCC SHRINKFILE命令執行日志收縮,這個命令必須提供數據庫日志文件的邏輯名,所以現在我們要想辦法將[DatabaseLogSize]表中的[log_logical_name]這一列值填上去,要找到數據庫文件的日志邏輯名,首先要找到數據庫的dbid。我們可以使用下面的語句找到[DatabaseLogSize]表中每個數據庫的dbid:
SELECT dbid,name FROM master..sysdatabases WHERE [name] IN (SELECT [dbname] FROM [Control].[DatabaseLogSize])
使用Update語句更新[DatabaseLogSize]表的[dbid]列即可:
UPDATE [Control].[DatabaseLogSize] SET [dbid]=T_DB_ID.[dbid] FROM [Control].[DatabaseLogSize] INNER JOIN ( SELECT [dbid],[name] FROM master..sysdatabases ) AS T_DB_ID ON [DatabaseLogSize].[dbname]=T_DB_ID.[name]
這樣[DatabaseLogSize]表的[dbid]列就有值了:
接下來我們可以通過下面的語句根據每個數據庫的dbid來找到每個數據庫日志文件的邏輯名稱,在本例中因為我們的每個數據庫只有一個日志文件所以每個數據庫只會找到一個日志文件邏輯名,如果你的情況是有數據庫有多個日志文件,那么有些數據庫就會找出多個日志文件邏輯名。
SELECT name,database_id FROM sys.master_files WHERE database_id IN (SELECT [dbid] FROM [Control].[DatabaseLogSize]) AND type = 1--這里的1表示我們要找的是.ldf日志文件,如果你要找的是.mdf數據庫文件將這里改為0即可
這里補充一個知識點:SQL Server中一個數據庫雖然可以通過創建多個數據文件組(FileGroup)來存放不同的.mdf和.ndf數據文件,但是日志文件組一個數據庫有且只能有一個,也就是說一個數據庫的所有.ldf日志文件都只能屬於一個文件組,在下圖中我們可以看到在SQL Server的SMSS管理器中如果你新建的是一個.ldf日志文件,那么其文件組這一列顯示的是不適用,說明我們無法將SQL Server的.ldf日志文件放到多個文件組中,SQL Server只允許每個數據庫擁有一個日志文件組,所有該數據庫的.ldf日志文件都放在這個默認的日志文件組中:
之后同樣我們用Update更新[DatabaseLogSize]表的[log_logical_name]列即可:
UPDATE [Control].[DatabaseLogSize] SET [log_logical_name]=T_DB_LOGNAME.[name] FROM [Control].[DatabaseLogSize] INNER JOIN ( SELECT [name],database_id FROM sys.master_files WHERE type = 1 ) AS T_DB_LOGNAME ON [DatabaseLogSize].[dbid]=T_DB_LOGNAME.database_id
這樣[DatabaseLogSize]表的[log_logical_name]列也就有值了:
三、收縮數據庫日志文件
接下來我們就可以根據[DatabaseLogSize]表的值來收縮數據庫日志文件了,這里我們使用循環判斷如果[DatabaseLogSize]表中的數據庫日志文件大小超過100MB了,我們就使用DBCC SHRINKFILE指令來收縮這個日志文件:
CREATE TABLE #LogNeedShrink ( [dbname] NVARCHAR(100) ,[log_logical_name] NVARCHAR(100) ) INSERT INTO #LogNeedShrink SELECT [dbname],[log_logical_name] FROM [Control].[DatabaseLogSize] WHERE [logsize]>100 --收縮超過100MB大小的日志文件 WHILE (SELECT COUNT(1) FROM #LogNeedShrink)>0 BEGIN DECLARE @CurrentDbName NVARCHAR(100) DECLARE @CurrentlogName NVARCHAR(100) SELECT TOP 1 @CurrentDbName=[dbname],@CurrentlogName=[log_logical_name] FROM #LogNeedShrink DECLARE @ShrinkScript NVARCHAR(1000); SET @ShrinkScript=N' USE '+QUOTENAME(@CurrentDbName,N'[')+N' DBCC SHRINKFILE (N'''+REPLACE(@CurrentlogName,N'''',N'''''')+N''' , 0, TRUNCATEONLY)' --這里如果不加TRUNCATEONLY參數,就是在告訴SQL Server將日志文件收縮為0,也就是讓SQL Server能收縮多少就收縮多少 --這里要轉義數據庫日志文件名稱中的單引號字符,因為DBCC SHRINKFILE的第一個參數本身就是在一個SQL字符串中,如果不轉義單引號字符會使得SQL字符串意外結束而報錯。另外我們用了QUOTENAME函數來轉義數據庫名稱中的非法字符 BEGIN TRY EXEC sp_executesql @ShrinkScript END TRY BEGIN CATCH --如果SQL語句報錯,輸出顯示報錯的SQL語句 PRINT @ShrinkScript RAISERROR (50001, -- Message id. 12, -- Severity, 1, -- State, N'Error occurred in above query.'); END CATCH DELETE FROM #LogNeedShrink WHERE [dbname]=@CurrentDbName AND [log_logical_name]=@CurrentlogName END DROP TABLE #LogNeedShrink
參考文獻:
DBCC SHRINKFILE (Transact-SQL)
DBCC SHRINKDATABASE (Transact-SQL)