多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SQL Server TSQL备份到共享目录:脚本、作业与避坑指南

SQL Server TSQL备份到共享目录:脚本、作业与避坑指南 简介这份资源面向SQL Server数据库管理员与运维开发人员聚焦数据库自动备份这一关键运维场景解决人工定时备份难以坚持、备份文件不便集中管理的问题。资源以docx文档形式交付共1个文件压缩包约457KB内容围绕TSQL脚本与SQL Server代理作业展开重点讲解如何将备份文件输出到局域网共享文件夹并给出完整的作业创建流程与可直接套用的备份语句。文档涵盖代理服务启动与登录账号配置、共享目录访问凭证记录、SSMS中新建作业的常规与步骤设置、备份命令中动态拼接日期时间戳的写法以及计划周期与执行频率的配置方法同时提示了服务重启后任务失效等常见排错思路。目前已有1162人学习下载适合需要为关键业务库搭建无人值守备份方案、希望掌握共享路径备份技巧的读者参考借鉴。1. 凌晨三点告警响起为什么我最终选了 TSQL 备份共享文件这条路凌晨三点被电话叫醒业务库所在的那台机器磁盘满了备份任务连续失败三天没人发现。这种事我经历过不止一次后来复盘发现问题往往不在数据库本身而在备份方案太依赖某个客户端工具、某个服务账号、某台机器的本地盘。一旦那台机器出问题备份链路就整条断掉。SQL Server 计划自动备份TSQL 备份共享文件版解决的正是这个痛点不依赖维护计划向导不依赖第三方备份软件直接用 TSQL 脚本把数据库备份写到网络共享目录再挂到 SQL Server 代理作业里按计划跑。适合谁适合手上管着几台到几十台 SQL Server、想用最轻量方式把备份落到独立存储、又不想引入额外组件的 DBA 和运维。它把备份这件事从某台机器的本地行为变成一条可审计、可迁移、可复制的脚本链路这才是它真正的价值。2. 拆开 TSQL 备份脚本BACKUP DATABASE 到底怎么写才不翻车2.1 最小可用备份语句与参数含义先看一条能跑通、能落盘、能验证的最小备份语句。很多人第一次写备份脚本直接抄一句BACKUP DATABASE就完事结果要么文件被覆盖要么路径不存在要么权限报错。下面这条是我在正式环境里用的基础模板-- 基础全量备份带时间戳文件名避免覆盖 DECLARE dbName SYSNAME NYourDB; -- 目标数据库名 DECLARE backupDir NVARCHAR(400) N\\FileServer\SQLBackup\; -- 共享目录结尾必须带反斜杠 DECLARE fileName NVARCHAR(400); DECLARE sql NVARCHAR(MAX); -- 用 yyyyMMdd_HHmmss 拼文件名保证每次备份不重名 SET fileName backupDir dbName N_FULL_ CONVERT(NVARCHAR(8), GETDATE(), 112) N_ REPLACE(CONVERT(NVARCHAR(8), GETDATE(), 108), N:, N) N.bak; SET sql NBACKUP DATABASE QUOTENAME(dbName) N TO DISK path N WITH INIT, COMPRESSION, CHECKSUM, STATS 10, NAME bname; EXEC sp_executesql sql, Npath NVARCHAR(400), bname NVARCHAR(200), path fileName, bname dbName N Full Backup;逻辑说明整段脚本先声明数据库名和共享目录再用GETDATE()拼出带日期和时间的文件名最后用sp_executesql参数化执行避免字符串拼接注入和路径转义问题。参数说明INIT表示覆盖同名备份集因为我们文件名带时间戳实际上不会撞名加上它更保险COMPRESSION在 SQL Server 2008 及以上支持能显著减小备份文件体积但会吃一点 CPUCHECKSUM会在备份时校验页校验和备份慢一点但恢复时能提前发现坏页STATS 10每完成 10% 输出一次进度方便在作业历史里看进度。共享目录结尾的反斜杠不能省否则拼出来的路径会变成\\FileServer\SQLBackupYourDB_FULL_...直接报错。2.2 差异备份与日志备份的衔接写法只有全量备份不够恢复时 RPO 会很大。常见做法是全量 差异 日志三层配合。差异备份依赖最近一次全量日志备份依赖完整恢复模式。下面这条差异备份脚本关键是DIFFERENTIAL和WITH INIT的组合-- 差异备份基于最近一次全量文件更小 DECLARE dbName SYSNAME NYourDB; DECLARE backupDir NVARCHAR(400) N\\FileServer\SQLBackup\; DECLARE fileName NVARCHAR(400); DECLARE sql NVARCHAR(MAX); SET fileName backupDir dbName N_DIFF_ CONVERT(NVARCHAR(8), GETDATE(), 112) N_ REPLACE(CONVERT(NVARCHAR(8), GETDATE(), 108), N:, N) N.diff; SET sql NBACKUP DATABASE QUOTENAME(dbName) N TO DISK path N WITH DIFFERENTIAL, INIT, COMPRESSION, CHECKSUM, STATS 10; EXEC sp_executesql sql, Npath NVARCHAR(400), path fileName;差异备份的坑在于如果全量备份失败差异备份会基于更早的全量恢复链就乱了。所以作业里全量和差异要有依赖关系或者至少在差异备份前检查最近一次全量是否成功。日志备份则要单独写BACKUP LOG不能带DIFFERENTIAL且数据库必须处于完整恢复模式-- 日志备份完整恢复模式下才有意义 DECLARE dbName SYSNAME NYourDB; DECLARE backupDir NVARCHAR(400) N\\FileServer\SQLBackup\; DECLARE fileName NVARCHAR(400); DECLARE sql NVARCHAR(MAX); SET fileName backupDir dbName N_LOG_ CONVERT(NVARCHAR(8), GETDATE(), 112) N_ REPLACE(CONVERT(NVARCHAR(8), GETDATE(), 108), N:, N) N.trn; SET sql NBACKUP LOG QUOTENAME(dbName) N TO DISK path N WITH INIT, COMPRESSION, CHECKSUM, STATS 10; EXEC sp_executesql sql, Npath NVARCHAR(400), path fileName;日志备份频率一般比全量高得多常见做法是每 15 到 30 分钟一次全量每天一次差异每 6 小时一次。具体频率取决于业务能容忍丢多少数据。日志文件不会因为备份就自动截断只有日志备份成功才会截断所以日志备份失败会直接导致日志文件涨满这是最常见的翻车点之一。2.3 把脚本挂进 SQL Server 代理作业脚本写好了下一步是让它自动跑。SQL Server 代理作业是最原生的方式不需要额外装东西。创建作业的 TSQL 如下USE msdb; GO -- 新建作业 EXEC dbo.sp_add_job job_name NDBA_FullBackup_YourDB, enabled 1, description N每日全量备份到共享目录; -- 添加步骤执行备份脚本 EXEC dbo.sp_add_jobstep job_name NDBA_FullBackup_YourDB, step_name NFullBackupStep, subsystem NTSQL, command NEXEC dbo.usp_FullBackup_YourDB;, -- 把脚本封装成存储过程 database_name Nmaster, retry_attempts 2, retry_interval 5; -- 添加计划每天凌晨 2 点 EXEC dbo.sp_add_schedule schedule_name NDaily_0200, freq_type 4, -- 每天 freq_interval 1, active_start_time 020000; EXEC dbo.sp_attach_schedule job_name NDBA_FullBackup_YourDB, schedule_name NDaily_0200; EXEC dbo.sp_add_jobserver job_name NDBA_FullBackup_YourDB;参数说明freq_type 4表示按天重复freq_interval 1表示每 1 天active_start_time 020000是凌晨 2 点。retry_attempts 2和retry_interval 5表示失败后重试 2 次、间隔 5 分钟这对网络共享偶发抖动很有用。把备份逻辑封装成存储过程再调用比把大段脚本直接塞进作业步骤更好维护改逻辑不用动作业。作业历史默认保留有限条数建议在代理属性里把历史记录调大否则出问题时看不到几天前的失败原因。3. 共享目录权限与网络路径备份写不过去多半卡在这3.1 SQL Server 服务账号对共享目录的权限备份写到\\FileServer\SQLBackup\这种 UNC 路径最容易翻车的地方是权限。SQL Server 服务运行在某个账号下这个账号必须对共享目录有写权限。注意是服务账号不是你登录 SSMS 用的账号。查看服务账号-- 查看 SQL Server 服务启动账号 SELECT servicename, service_account FROM sys.dm_server_services WHERE servicename LIKE SQL Server (%;如果服务账号是NT Service\MSSQLSERVER这种虚拟账号它在远程文件服务器上没有身份需要给文件服务器上的共享目录授权这个账号或者改用域账号运行 SQL Server 服务。常见做法是在文件服务器上给共享目录授予 SQL Server 服务账号修改权限同时确保共享权限和 NTFS 权限两层都放行。只给共享权限不给 NTFS 权限或者反过来都会失败。验证方法是在 SQL Server 所在机器上用服务账号身份访问共享目录但更直接的办法是跑一次备份看报错。3.2 用 xp_cmdshell 还是直接 BACKUP TO DISK有人会想用xp_cmdshell调copy命令把本地备份复制到共享我不推荐。原因有三xp_cmdshell默认关闭开启有安全风险多一步复制就多一个失败点本地盘还要先落一份磁盘压力翻倍。直接BACKUP DATABASE ... TO DISK \\FileServer\...是 SQL Server 原生支持的走的是 SQL Server 自己的文件写入通道效率更高链路更短。唯一要注意的是网络带宽和延迟备份大库时共享目录的写入速度会成为瓶颈。如果共享目录在慢速链路上备份时间可能比本地盘长好几倍这时候要考虑压缩备份或者调整备份窗口。3.3 备份文件命名与保留策略文件名带时间戳解决了覆盖问题但会带来新问题文件越积越多共享目录迟早爆掉。保留策略常见有两种按天数删旧文件或者按文件数量保留最近 N 份。用 TSQL 删旧文件需要借助xp_delete_file或者xp_cmdshell前者是 SQL Server 内置的扩展存储过程专门用来删备份文件-- 删除 7 天前的 .bak 文件 DECLARE backupDir NVARCHAR(400) N\\FileServer\SQLBackup\; DECLARE cutoff DATETIME DATEADD(DAY, -7, GETDATE()); EXEC master.sys.xp_delete_file 0, -- 0 表示备份文件 backupDir, Nbak, -- 扩展名不带点 cutoff, 1; -- 包含子目录参数说明第一个参数 0 代表备份文件1 代表维护计划文件第三个参数是扩展名不带点第四个参数是截止时间早于这个时间的文件会被删。xp_delete_file只删它认识的备份文件不会误删其他文件比xp_cmdshell安全。注意它删的是文件修改时间早于截止时间的文件不是文件名里的时间戳所以如果文件被复制过、修改时间变了可能删不掉或者误删这点要心里有数。4. 避坑与排查备份作业失败时先看这五条4.1 现象作业报操作系统错误 5拒绝访问原因SQL Server 服务账号对共享目录没有写权限或者共享权限与 NTFS 权限只配了一层。解决在文件服务器上确认共享权限和 NTFS 权限都授予了服务账号修改级别虚拟账号要换成域账号或给机器账号授权。改完权限后不用重启 SQL Server直接重跑作业验证。4.2 现象备份文件大小正常但恢复时报备份集不完整原因备份过程中网络中断或磁盘写满备份文件写了一半。CHECKSUM能在备份时发现页校验问题但网络中断导致的截断不一定报错。解决备份后加一步RESTORE VERIFYONLY验证-- 验证备份文件可恢复 RESTORE VERIFYONLY FROM DISK N\\FileServer\SQLBackup\YourDB_FULL_20250101_020000.bak WITH CHECKSUM;把这一步做成作业的后续步骤验证失败就发告警。别等真要恢复时才发现备份是坏的那时候没有后悔药。4.3 现象日志文件持续增长磁盘告警原因日志备份失败或没配日志备份数据库处于完整恢复模式但日志不截断。解决先查日志空间使用-- 查看各数据库日志空间使用 DBCC SQLPERF(LOGSPACE);如果日志使用率接近 100%先做一次日志备份把日志截断再检查日志备份作业为什么没跑。常见原因是日志备份作业被禁用、计划时间冲突、或者共享目录满了导致日志备份也写不进去。4.4 现象备份作业偶尔失败重试就成功原因网络共享偶发抖动、文件服务器瞬时负载高、DNS 解析慢。解决作业步骤里设置重试次数和间隔前面sp_add_jobstep里的retry_attempts和retry_interval就是干这个的。另外可以把备份时间错开业务高峰减少网络争用。如果频繁抖动要考虑共享目录所在存储的健康状况。4.5 现象备份成功但作业历史里看不到详细信息原因SQL Server 代理历史记录条数限制或者作业步骤输出没被记录。解决在代理属性里把作业历史记录日志的最大行数调大至少保留够覆盖一个备份周期。另外在备份脚本里用STATS 10输出进度这些信息会进作业历史排查时有用。别小看这一步出问题时没有历史记录等于黑匣子。5. 进阶技巧用存储过程 配置表把备份脚本管起来写到这一步如果每个库都复制一份脚本维护会疯掉。我一般会把备份逻辑封装成一个通用存储过程数据库名、备份类型、保留天数从配置表读。配置表长这样CREATE TABLE dbo.BackupConfig ( DBName SYSNAME NOT NULL PRIMARY KEY, BackupType VARCHAR(10) NOT NULL, -- FULL / DIFF / LOG BackupDir NVARCHAR(400) NOT NULL, RetainDays INT NOT NULL DEFAULT 7, IsEnabled BIT NOT NULL DEFAULT 1 );然后存储过程按配置循环执行CREATE OR ALTER PROCEDURE dbo.usp_RunBackup BackupType VARCHAR(10) AS BEGIN SET NOCOUNT ON; DECLARE db SYSNAME, dir NVARCHAR(400), retain INT; DECLARE cur CURSOR LOCAL FAST_FORWARD FOR SELECT DBName, BackupDir, RetainDays FROM dbo.BackupConfig WHERE BackupType BackupType AND IsEnabled 1; OPEN cur; FETCH NEXT FROM cur INTO db, dir, retain; WHILE FETCH_STATUS 0 BEGIN BEGIN TRY EXEC dbo.usp_BackupOneDB db, BackupType, dir; EXEC dbo.usp_CleanOldBackup dir, retain; END TRY BEGIN CATCH -- 记录失败日志便于后续排查 INSERT INTO dbo.BackupLog(DBName, BackupType, ErrMsg, LogTime) VALUES(db, BackupType, ERROR_MESSAGE(), GETDATE()); END CATCH FETCH NEXT FROM cur INTO db, dir, retain; END CLOSE cur; DEALLOCATE cur; END这样加库、改保留天数、临时禁用某个库的备份都只改配置表不动脚本。作业里只留三个步骤全量、差异、日志各自调usp_RunBackup传不同参数。验证方法也简单改一条配置手动执行存储过程看备份文件有没有按预期生成、旧文件有没有被清掉。从那以后我每次上线新的备份策略都强制先在一个非关键库上跑通全流程再改配置表放量。备份这东西平时不出声出事时就是最后一道防线值得多花半小时把脚本管好。希望帮到你。本文还有配套的精品资源点击获取
返回列表