MsSql

关注公众号 jb51net

关闭
首页 > 数据库 > MsSql > sql server日志文件收缩

SQL SERVER数据库日志文件收缩图文详解

作者:王依华

数据库收缩的主要目的之一是释放未被使用的空间,随着数据库的日常操作,如插入、更新、删除等,下面这篇文章主要介绍了SQL SERVER数据库日志文件收缩的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、为什么需要收缩日志文件?

在 SQL Server 中,事务日志文件(.ldf)会记录所有数据库事务操作(如增删改、事务提交 / 回滚),用于故障恢复和数据一致性保障。但在以下场景中,日志文件可能会异常膨胀:

  1. FULL 恢复模式下未定期备份日志:日志会持续累积事务记录,无法自动释放空间;
  2. 长事务未提交:如长时间运行的 UPDATE/DELETE 语句,会锁定日志片段,导致无法截断;
  3. 数据库镜像 / 复制配置异常:日志记录因同步延迟被占用,无法正常回收。

日志文件过度膨胀会占用大量磁盘空间,甚至导致磁盘满额、数据库性能下降。此时需通过 “收缩操作” 释放未使用的空间,但需注意:收缩仅适用于 “临时清理空间”,需先排查膨胀根源(如完善备份计划),避免频繁操作导致文件碎片化。

二、可视化操作(SSMS 界面)

适用于Windows系统、新手或单数据库少量操作,以 SQL Server 2012(版本 11.0)、数据库 Db1 为例,核心步骤分三步:切换恢复模式→收缩日志→恢复原模式

1. 将恢复模式调整为 “简单”

简单恢复模式(SIMPLE)的核心特点是 “事务日志自动截断”—— 检查点(Checkpoint)后,会自动释放已提交事务的日志空间,无需手动备份日志,这是后续收缩日志的前提(FULL 模式下日志无法直接截断)。

  1. 打开 SQL Server Management Studio(SSMS),在 “对象资源管理器” 中找到目标数据库 Db1右键点击,选择 “属性(R)”;
  2. 在 “数据库属性 - Db1” 窗口的左侧 “选择页” 中,点击 “选项”;
  3. 在右侧 “恢复模式(M)” 下拉框中,将默认的 “完整” 改为 “简单”;
  4. 点击 “确定” 保存设置,此时数据库会立即切换到简单恢复模式。

2. 收缩数据库日志文件

切换到简单模式后,日志中未使用的空间已标记为 “可回收”,需通过 “收缩文件” 操作释放磁盘空间。

操作步骤:

  1. 右键点击 Db1 数据库,选择 “任务(T)”→“收缩(S)”→“文件(F)”;
  2. 在 “收缩文件 - Db1” 窗口中,进行以下配置:
    • 文件类型(T):下拉选择 “日志”(默认是 “数据”,需手动切换,避免收缩 .mdf 数据文件);
    • 文件名(F):自动显示当前数据库的日志文件(如 Db1_log),无需修改;
    • 收缩操作:选择 “释放未使用的空间(R)”(仅释放未使用的尾部空间,不移动日志数据,对性能影响最小);
      • 不建议选择 “将文件收缩到(K)”:该选项会强制将日志压缩到指定大小(如 3MB),可能导致日志数据页重组,产生大量碎片化,影响后续事务性能;
      • 不建议选择 “通过将数据迁移到同一文件组中的其他文件来清空文件(E)”:仅适用于删除日志文件的场景,常规收缩无需使用;
  3. 点击 “确定”,SSMS 会执行收缩操作,此时日志文件中未使用的空间会被释放。

3.将恢复模式调整回“完整”。

简单恢复模式虽便于收缩日志,但仅支持 “恢复到最近完整备份”,无法实现 “时间点恢复”(如恢复到故障前 10 分钟的数据),不符合生产环境对数据安全性的要求。因此收缩完成后,需立即切回完整恢复模式。

操作步骤:

  1. 重复上述“1. 将恢复模式调整为 “简单””的 1-2 步,打开 “数据库属性 - Db1” 的 “选项” 页;
  2. 将 “恢复模式” 从 “简单” 改回 “完整”,点击 “确定”;
  3. 关键补充:切换回完整模式后,需立即执行一次 “完整备份”(右键 Db1→“任务”→“备份”,选择 “完整” 备份类型),否则后续的日志备份会失败 —— 因为简单模式会断裂 “日志链”,完整备份是重建日志链、保障时间点恢复能力的前提。

三、代码操作(T-SQL)

适用于批量操作(如多数据库同时收缩)或自动化脚本(如通过作业定期执行),相比可视化操作更高效、可复用。代码分为 “单数据库” 和 “多数据库” 两种场景,核心逻辑与可视化操作一致:查日志名→切简单模式→收缩日志→切完整模式

1. 单数据库收缩(以 Db1 为例)

0. 前置步骤:查询日志文件逻辑名称

收缩日志前,需先确认目标数据库的日志文件逻辑名称,避免因名称错误导致收缩失败。

-- 0. 查询数据库 Db1 的日志文件逻辑名称
SELECT 
    name AS 日志文件逻辑名称,  -- 逻辑名称(收缩时需用此名称)
    physical_name AS 日志文件物理路径,  -- 物理文件路径(可确认文件位置)
    size/128.0 AS 当前大小_MB,  -- 转换为 MB(SQL Server 中 size 单位是 8KB 页)
    FILEPROPERTY(name, 'SpaceUsed')/128.0 AS 已使用大小_MB  -- 计算实际使用空间
FROM 
    sys.database_files  -- 系统视图,存储数据库文件信息
WHERE 
    type = 1;  -- type=1 表示日志文件,type=0 表示数据文件

1. 切换到简单恢复模式

    
-- 1. 将数据库 Db1 的恢复模式设置为“简单”
ALTER DATABASE Db1 
SET RECOVERY SIMPLE;  -- 未加 WITH NO_WAIT,默认会等待数据库锁释放(适合单库操作,避免直接报错)

2. 收缩日志文件

-- 2. 收缩 Db1 的日志文件(需替换为步骤 0 查询到的日志文件逻辑名称)
DBCC SHRINKFILE (
    N'Db1_log',  -- 第一个参数:日志文件逻辑名称(N 表示 Unicode 字符串,避免中文/特殊字符问题)
    TRUNCATEONLY  -- 第二个参数:仅截断未使用的尾部空间,不移动日志数据
);

代码解释:

3. 切换回完整恢复模式

-- 3. 将数据库 Db1 的恢复模式设置为“完整”,并添加 WITH NO_WAIT 选项
ALTER DATABASE Db1 
SET RECOVERY FULL 
WITH NO_WAIT;  -- 若数据库被其他进程锁定(如查询/备份),不等待直接报错(适合脚本自动化,避免无限等待)

补充说明:

2. 多数据库批量收缩

当需要同时收缩多个数据库时,用 “游标 + 动态 SQL” 实现循环处理,同时添加错误捕获(可根据需要将执行记录保存到日志表中),避免单个数据库失败导致整个脚本中断。

DECLARE @DBs TABLE (DBName NVARCHAR(128));
INSERT INTO @DBs (DBName)
VALUES 
    ('Db1'),   
    ('Db2');  
--Tip:再次维护需要收缩的数据库名称

DECLARE @CurrentDB NVARCHAR(128);
DECLARE @LogFileName NVARCHAR(128);
DECLARE @SQL NVARCHAR(MAX);

--使用游标循环处理各个数据库@DBs

DECLARE DB_Cursor CURSOR FOR 
SELECT DBName FROM @DBs;

OPEN DB_Cursor;
FETCH NEXT FROM DB_Cursor INTO @CurrentDB;

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT '------------------------------------------------';
    PRINT '开始处理数据库:' + @CurrentDB;

    BEGIN TRY
        --1.切换数据库为简单恢复模式
        SET @SQL = N'ALTER DATABASE ' + QUOTENAME(@CurrentDB) + N' SET RECOVERY SIMPLE WITH NO_WAIT;';
        EXEC sp_executesql @SQL;
        PRINT @CurrentDB + ' 已切换为简单恢复模式';

        --2.查询日志文件逻辑名称
        SET @SQL = N'
            USE ' + QUOTENAME(@CurrentDB) + N';
            SELECT TOP 1 @LogNameOUT = name 
            FROM sys.database_files 
            WHERE type = 1;  -- type=1 表示日志文件
        ';
        EXEC sp_executesql @SQL, 
            N'@LogNameOUT NVARCHAR(128) OUTPUT', 
            @LogNameOUT = @LogFileName OUTPUT;

        --3.收缩日志文件(释放未使用空间)
        IF @LogFileName IS NOT NULL
        BEGIN
            SET @SQL = N'
                USE ' + QUOTENAME(@CurrentDB) + N';
                DBCC SHRINKFILE (N''' + @LogFileName + N''', TRUNCATEONLY);
            ';
            EXEC sp_executesql @SQL;
            PRINT @CurrentDB + ' 的日志文件 "' + @LogFileName + '" 收缩完成';
        END
        ELSE
        BEGIN
            PRINT @CurrentDB + ' 未找到日志文件,跳过收缩';
        END

        --4.切换回完整恢复模式
        SET @SQL = N'ALTER DATABASE ' + QUOTENAME(@CurrentDB) + N' SET RECOVERY FULL WITH NO_WAIT;';
        EXEC sp_executesql @SQL;
        PRINT @CurrentDB + ' 已切换回完整恢复模式';

    END TRY

    --报错处理方式
    BEGIN CATCH
        
        PRINT @CurrentDB + ' 处理失败:';
        PRINT '错误消息:' + ERROR_MESSAGE();
    END CATCH

    FETCH NEXT FROM DB_Cursor INTO @CurrentDB;
END

CLOSE DB_Cursor;
DEALLOCATE DB_Cursor;

PRINT '------------------------------------------------';
PRINT '所有数据库处理完毕';

四、知识延伸

1. 为什么收缩前必须切换恢复模式?

2. 生产环境收缩日志的注意事项

  1. 避免业务高峰期执行:收缩操作会产生 IO 开销,若在高峰期执行,可能导致数据库响应延迟;建议在凌晨或低峰期执行;
  2. 收缩后必做完整备份:切换回 FULL 模式后,日志链已断裂,需立即执行完整备份,否则后续日志备份会失败,无法实现时间点恢复;
  3. 不建议定期收缩:频繁收缩会导致日志文件碎片化(日志数据分散在多个磁盘块中),后续事务写入时需频繁寻址,降低性能;正确做法是 “排查日志膨胀根源”(如完善日志备份计划,设置每 15-30 分钟备份一次日志);
  4. 监控日志文件大小:通过 SSMS 的 “数据库→属性→文件”,设置日志文件的 “自动增长”(如每次增长 100MB,而非 “按百分比增长”),避免频繁小幅度增长导致碎片化。

3. 常见错误与解决方案

错误现象原因解决方案
执行 ALTER DATABASE 时提示 “无法对数据库放置锁”数据库被其他进程占用(如长事务、备份、查询)1. 用 sp_who2 查询占用进程的 session_id;2. 若为无关查询,用 KILL session_id 终止;3. 若为备份,等待备份完成后再执行
DBCC SHRINKFILE 执行后日志大小无变化1. 日志中仍有活动事务;2. 未切换到简单模式1. 执行 DBCC OPENTRAN(@CurrentDB) 查看未提交事务,终止后重试;2. 确认恢复模式已切换为 “简单”
切换回 FULL 模式后日志备份失败未执行完整备份,日志链断裂立即执行一次 “完整备份”,再执行日志备份

总结 

到此这篇关于SQL SERVER数据库日志文件收缩的文章就介绍到这了,更多相关sql server日志文件收缩内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文