Linux 环境下 SQL Server 自动收缩日志作业创建脚本(Shell 版)

随着SQL Server在Linux平台的普及,很多运维人员需要在Linux服务器上实现数据库日志的自动收缩。本文将原SQL脚本改造为适配Linux环境的Shell脚本,兼容SQL Server 2017及以上Linux版本(2017/2019/2022),保留全版本适配、安全合规、易维护的核心特性,同时贴合Linux系统的操作习惯。

一、完整Shell脚本

bash 复制代码
#!/bin/bash
# =========================================================================
# 脚本名称:sqlserver_shrink_log_job.sh
# 适用环境:Linux + SQL Server 2017/2019/2022(x64)
# 前置条件:
#  1. Linux服务器已安装SQL Server,且启动sqlserver服务
#  2. 已安装sqlcmd工具(/opt/mssql-tools/bin/sqlcmd)
#  3. master库已创建dbo.ShrinkUser_DATABASESLogFile存储过程
#  4. 执行脚本的Linux用户具备执行sqlcmd的权限
# 核心功能:在Linux的SQL Server中创建自动收缩日志的定时作业
# 作者:运维研发组
# 日期:2025-12-16
# =========================================================================

set -euo pipefail  # Shell严格模式:未定义变量报错、管道失败则脚本退出

# ======================== 自定义配置参数(根据实际需求修改) ========================
export SQL_SERVER="localhost"          # SQL Server实例地址(本地填localhost,远程填IP/主机名)
export SQL_USER="sa"                   # SQL登录账户(建议替换为低权限运维账户)
# 建议通过环境变量传入密码,避免硬编码(执行前执行:export SQL_PWD="你的密码")
# export SQL_PWD="YourStrongPassword"   # 注释:生产环境请勿直接写入脚本!
export JOB_NAME="ShrinkFile_DB_Logfile"                    # 作业名称
export JOB_CATEGORY="Database Maintenance"                 # 作业分类
export SCHEDULE_NAME="Shrink_DB_Logfile_Schedule"          # 调度名称
export DAILY_EXEC_TIME="050000"                            # 每日执行时间(HHMMSS,050000=凌晨5点)
export DELETE_EXIST_JOB="1"                                # 是否删除已存在的同名作业(1=删除,0=不删除)
export NOTIFY_LEVEL="2"                                    # 作业失败时记录事件日志(0=不记录,2=失败记录)

# ======================== 依赖检查 ========================
# 检查sqlcmd是否安装
if ! command -v /opt/mssql-tools/bin/sqlcmd &> /dev/null; then
    echo "【错误】未找到sqlcmd工具,请先安装:sudo apt-get install mssql-tools unixodbc-dev(Debian/Ubuntu)或 sudo yum install mssql-tools unixODBC-devel(RHEL/CentOS)"
    exit 1
fi

# 检查密码是否传入
if [ -z "${SQL_PWD:-}" ]; then
    echo "【错误】未设置SQL_PWD环境变量,请执行:export SQL_PWD='你的SQL账户密码' 后再运行脚本"
    exit 1
fi

# 检查SQL Server服务是否运行(可选)
if ! systemctl is-active --quiet mssql-server; then
    echo "【警告】SQL Server服务未运行,建议先启动:sudo systemctl start mssql-server"
    read -p "是否继续执行脚本?(y/n) " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

# ======================== 构建TSQL脚本内容 ========================
TSQL_SCRIPT=$(cat << EOF
SET NOCOUNT ON;
SET XACT_ABORT ON;

-- 检查依赖的收缩存储过程是否存在
IF NOT EXISTS (
    SELECT 1 
    FROM master.sys.objects 
    WHERE name = N'ShrinkUser_DATABASESLogFile' 
      AND type = N'P' 
      AND schema_id = SCHEMA_ID(N'dbo')
)
BEGIN
    RAISERROR(N'前置条件不满足:master库中未找到dbo.ShrinkUser_DATABASESLogFile存储过程!',16,1);
    RETURN;
END;

-- 检查并处理已存在的同名作业
IF EXISTS (
    SELECT 1 
    FROM msdb.dbo.sysjobs 
    WHERE name = N'$JOB_NAME'
)
BEGIN
    IF $DELETE_EXIST_JOB = 1
    BEGIN
        EXEC msdb.dbo.sp_delete_job @job_name = N'$JOB_NAME', @delete_unused_schedules = 1;
        PRINT N'已删除同名作业:$JOB_NAME';
    END
    ELSE
    BEGIN
        RAISERROR(N'作业【%s】已存在,若需重新创建请将DELETE_EXIST_JOB设为1',16,1,N'$JOB_NAME');
        RETURN;
    END
END

BEGIN TRY
    BEGIN TRANSACTION;

    -- 创建作业分类(若不存在)
    IF NOT EXISTS (
        SELECT 1 
        FROM msdb.dbo.syscategories 
        WHERE name = N'$JOB_CATEGORY' 
          AND category_class = 1
    )
    BEGIN
        DECLARE @ReturnCode INT = 0;
        EXEC @ReturnCode = msdb.dbo.sp_add_category 
            @class = N'JOB', 
            @type = N'LOCAL', 
            @name = N'$JOB_CATEGORY';
        
        IF @ReturnCode <> 0
        BEGIN
            RAISERROR(N'创建作业分类【%s】失败,返回码:%d',16,1,N'$JOB_CATEGORY',@ReturnCode);
        END
        PRINT N'已创建作业分类:$JOB_CATEGORY';
    END

    -- 创建核心作业
    DECLARE @JobId BINARY(16), @ReturnCode INT = 0;
    EXEC @ReturnCode = msdb.dbo.sp_add_job 
        @job_name = N'$JOB_NAME',
        @enabled = 1,
        @notify_level_eventlog = $NOTIFY_LEVEL,
        @notify_level_email = 0,
        @notify_level_netsend = 0,
        @notify_level_page = 0,
        @delete_level = 0,
        @description = N'Linux环境自动收缩用户数据库日志文件(适配SQL Server Linux版),依赖master.dbo.ShrinkUser_DATABASESLogFile存储过程',
        @category_name = N'$JOB_CATEGORY',
        @owner_login_name = N'$SQL_USER',
        @job_id = @JobId OUTPUT;

    IF @ReturnCode <> 0
    BEGIN
        RAISERROR(N'创建作业【%s】失败,返回码:%d',16,1,N'$JOB_NAME',@ReturnCode);
    END
    PRINT N'已创建作业主体:$JOB_NAME';

    -- 添加作业执行步骤
    EXEC @ReturnCode = msdb.dbo.sp_add_jobstep 
        @job_id = @JobId,
        @step_name = N'Execute_Shrink_Log_Procedure',
        @step_id = 1,
        @cmdexec_success_code = 0,
        @on_success_action = 1,
        @on_success_step_id = 0,
        @on_fail_action = 2,
        @on_fail_step_id = 0,
        @retry_attempts = 0,
        @retry_interval = 0,
        @os_run_priority = 0,
        @subsystem = N'TSQL',
        @command = N'
            BEGIN TRY
                PRINT N''开始执行日志收缩存储过程:'' + CONVERT(NVARCHAR(30), GETDATE(), 120);
                EXEC master.dbo.ShrinkUser_DATABASESLogFile;
                PRINT N''日志收缩存储过程执行完成:'' + CONVERT(NVARCHAR(30), GETDATE(), 120);
            END TRY
            BEGIN CATCH
                DECLARE @ErrMsg NVARCHAR(4000) = ERROR_MESSAGE();
                RAISERROR(N''收缩日志失败:%s'',16,1,@ErrMsg);
            END CATCH
        ',
        @database_name = N'master',
        @flags = 0;

    IF @ReturnCode <> 0
    BEGIN
        RAISERROR(N'添加作业步骤失败,返回码:%d',16,1,@ReturnCode);
    END
    PRINT N'已添加作业执行步骤';

    -- 配置作业调度
    EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule 
        @job_id = @JobId,
        @name = N'$SCHEDULE_NAME',
        @enabled = 1,
        @freq_type = 8,
        @freq_interval = 127,
        @freq_subday_type = 1,
        @freq_subday_interval = 0,
        @freq_relative_interval = 0,
        @freq_recurrence_factor = 1,
        @active_start_date = CONVERT(INT, REPLACE(CONVERT(VARCHAR(10), GETDATE(), 112), '-', '')),
        @active_end_date = 99991231,
        @active_start_time = $DAILY_EXEC_TIME,
        @active_end_time = 235959;

    IF @ReturnCode <> 0
    BEGIN
        RAISERROR(N'配置作业调度失败,返回码:%d',16,1,@ReturnCode);
    END
    PRINT N'已配置作业调度:每日$DAILY_EXEC_TIME执行';

    -- 关联作业到本地服务器(Linux下SQL Server实例默认名为MSSQLSERVER)
    EXEC @ReturnCode = msdb.dbo.sp_add_jobserver 
        @job_id = @JobId,
        @server_name = N'(local)';

    IF @ReturnCode <> 0
    BEGIN
        RAISERROR(N'关联作业到服务器失败,返回码:%d',16,1,@ReturnCode);
    END
    PRINT N'已将作业关联到本地服务器';

    COMMIT TRANSACTION;
    PRINT N'============================================';
    PRINT N'作业【$JOB_NAME】创建成功!';
    PRINT N'============================================';
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;

    DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorSeverity INT = ERROR_SEVERITY(),
            @ErrorState INT = ERROR_STATE();

    RAISERROR(N'创建作业失败:%s',@ErrorSeverity,@ErrorState,@ErrorMessage);
END CATCH

SET NOCOUNT OFF;
EOF
)

# ======================== 执行TSQL脚本 ========================
echo "【信息】开始执行TSQL脚本,创建自动收缩日志作业..."
/opt/mssql-tools/bin/sqlcmd -S "$SQL_SERVER" -U "$SQL_USER" -P "$SQL_PWD" -Q "$TSQL_SCRIPT"

# 检查执行结果
if [ $? -eq 0 ]; then
    echo "【成功】作业创建脚本执行完成!"
    echo "【提示】可通过以下命令查看作业:"
    echo "  /opt/mssql-tools/bin/sqlcmd -S $SQL_SERVER -U $SQL_USER -P $SQL_PWD -Q 'SELECT name, enabled FROM msdb.dbo.sysjobs WHERE name = N''$JOB_NAME'';'"
    echo "【提示】查看作业执行历史:"
    echo "  /opt/mssql-tools/bin/sqlcmd -S $SQL_SERVER -U $SQL_USER -P $SQL_PWD -Q 'EXEC msdb.dbo.sp_help_jobhistory @job_name = N''$JOB_NAME'';'"
else
    echo "【错误】作业创建脚本执行失败!"
    exit 1
fi

二、使用步骤

1. 前置准备

  • 确保Linux服务器已安装SQL Server并启动服务;
  • 安装sqlcmd工具:
    • Debian/Ubuntu:sudo apt-get update && sudo apt-get install -y mssql-tools unixodbc-dev
    • RHEL/CentOS:sudo yum install -y mssql-tools unixODBC-devel
  • master库创建dbo.ShrinkUser_DATABASESLogFile存储过程(核心收缩逻辑);
  • 创建低权限SQL运维账户(可选,替代sa)。

2. 脚本部署与执行

bash 复制代码
# 1. 将脚本保存为sqlserver_shrink_log_job.sh
vim sqlserver_shrink_log_job.sh

# 2. 添加执行权限
chmod +x sqlserver_shrink_log_job.sh

# 3. 设置SQL密码环境变量(关键:避免硬编码)
export SQL_PWD="你的SQL账户密码"

# 4. 执行脚本
./sqlserver_shrink_log_job.sh

3. 验证作业创建结果

bash 复制代码
# 查看创建的作业
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SQL_PWD" -Q "SELECT name, enabled FROM msdb.dbo.sysjobs WHERE name = N'ShrinkFile_DB_Logfile';"

# 手动执行作业(测试)
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SQL_PWD" -Q "EXEC msdb.dbo.sp_start_job @job_name = N'ShrinkFile_DB_Logfile';"

# 查看作业执行历史
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SQL_PWD" -Q "EXEC msdb.dbo.sp_help_jobhistory @job_name = N'ShrinkFile_DB_Logfile';"

三、重要注意事项

1. 安全规范

  • 禁止硬编码密码:永远不要将SQL密码直接写入脚本,通过环境变量或配置文件传入;
  • 最小权限原则 :避免使用sa账户,创建专门的运维账户(授予SQLAgentOperatorRoledb_owner(master库)等最小权限);
  • 脚本权限 :限制脚本的读取权限(chmod 700 sqlserver_shrink_log_job.sh),避免密码泄露。

2. 性能与业务风险

  • 收缩日志的适用场景:仅用于日志异常增长的应急场景,不要作为常规运维手段;
  • 完整恢复模式处理 :若数据库为完整恢复模式,需在收缩前执行日志备份(BACKUP LOG),否则收缩无效;
  • 避免碎片化:频繁收缩会导致日志文件碎片化,建议收缩后调整日志文件的自动增长规则(如按固定大小增长,而非百分比)。

3. 运维监控

  • 定期检查作业执行历史,确认收缩逻辑正常运行;
  • 监控数据库日志文件大小,从根源解决增长问题(如优化长事务、调整恢复模式、配置定时日志备份);
  • Linux下可通过cron定时检查作业状态(可选)。
相关推荐
TDengine (老段)3 小时前
山东港口科技借助 TDengine 构建智慧港口“数据基石”
大数据·数据库·物联网·时序数据库·tdengine
徐徐图之!3 小时前
五、【阶段一运维基础 之 干货!!!】安装 Vmware 和 CentOS
linux·运维·centos
Hello.Reader3 小时前
Flink SQL INSERT 语句单表写入、多表分流、分区覆盖与 StatementSet
数据库·sql·flink
马克学长3 小时前
SSM小型餐饮综合管理系统j1c7m(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·小型餐饮管理系统·菜品管理·员工考勤
石小千3 小时前
OpenProject服务的备份与恢复
运维
scriptsboy4 小时前
Halo Docker 迁移方法
运维·docker·容器
Felven4 小时前
华为昇腾310P模型转换失败问题解决
linux·python·模型训练·昇腾·310p
葱卤山猪4 小时前
【Qt】心跳检测与粘包处理:打造稳定可靠的TCP Socket通信
开发语言·数据库·qt