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定时检查作业状态(可选)。
相关推荐
十日十行1 小时前
Linux和window共享文件夹
linux
李广坤4 小时前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
木心月转码ing8 小时前
WSL+Cpp开发环境配置
linux
爱可生开源社区1 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
蝎子莱莱爱打怪1 天前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes
崔小汤呀1 天前
最全的docker安装笔记,包含CentOS和Ubuntu
linux·后端
随逸1771 天前
《从零搭建NestJS项目》
数据库·typescript
何中应1 天前
vi编辑器使用
linux·后端·操作系统
何中应1 天前
Linux进程无法被kill
linux·后端·操作系统
何中应1 天前
rm-rf /命令操作介绍
linux·后端·操作系统