简介:这份资源面向SQL Server数据库管理员与运维开发人员,聚焦数据库自动备份这一关键运维场景,解决人工定时备份难以坚持、数据恢复缺乏保障的问题。资源以docx文档形式交付,共1个文件,压缩包约457KB,内容围绕TSQL脚本与SQL Server代理作业展开,涵盖代理服务启动与登录账号配置、局域网共享文件夹权限与凭证处理、作业步骤与计划周期设置等完整流程,并给出针对数据库KJ_Standard_E的完整备份语句示例,利用动态SQL拼接日期时间戳生成备份文件名。读者可据此掌握将备份文件输出至共享目录的自动化方案,理解代理服务与作业调度的配合要点,并参考其中的排错思路与配置细节,快速搭建无需人工值守的定时备份机制。目前已有1162人学习下载,适合需要提升数据安全与可恢复性保障能力的运维人员参考。
1. 计划自动备份这件事,为什么手搓 TSQL 比维护计划更稳
很多用 SQL Server 的团队都经历过这种场景:数据库跑得好好的,某天磁盘满了、误删了表、或者要临时搭一套测试环境,才发现最近一次能用的备份是三个月前某个同事手动点出来的。于是开始找自动备份方案,SSMS 里的维护计划向导点几下就能生成作业,看起来很美,但真到生产环境里,维护计划经常出玄学问题——子计划执行顺序不透明、清理任务和备份任务打架、跨版本迁移时作业步骤丢失。我后来干脆放弃维护计划,改成用纯 TSQL 脚本加 SQL Server 代理作业来驱动,脚本自己控制备份路径、文件命名、保留策略和日志记录,出问题一眼能定位。
这篇讲的就是这套做法:用 TSQL 的BACKUP DATABASE语句,把备份文件写到共享目录(网络共享或本机共享路径),再挂到代理作业上按计划跑。它解决的是「无人值守、可追溯、可迁移」三个诉求——脚本是文本,能进版本库,换台服务器改几个变量就能复用。适合谁?适合手里有 SQL Server、又不想被维护计划黑匣子绑架的 DBA 和后端开发。下面从原理到脚本到踩坑,一步步拆开。
2. 备份语句与共享路径:先把单次备份跑通再谈自动化
2.1 BACKUP DATABASE 的关键参数到底在控制什么
BACKUP DATABASE看着简单,但几个参数直接决定备份能不能用、恢复快不快。核心是这几个:WITH INIT或WITH NOINIT决定是覆盖还是追加到已有备份文件;COMPRESSION决定是否压缩(企业版默认支持,标准版要看版本);CHECKSUM让备份过程校验页完整性,恢复时能提前发现坏页;STATS = 10让进度每 10% 报一次,方便在作业历史里看进度。还有一个容易被忽略的COPY_ONLY,它不影响差异备份链,做临时备份时必加,否则会把差异备份的基准打乱。
共享路径这块,SQL Server 服务账户必须对目标共享有写权限。注意是服务账户,不是你自己登录 Windows 的账户。很多人本地测试用自己账号能写,作业一跑就报「拒绝访问」,就是没搞清这一点。路径写法上,UNC 路径\\服务器名\共享名\子目录\是标准做法,本机共享也可以用\\本机名\共享名\,但别用映射盘符(比如Z:\),因为映射盘符是会话级的,服务账户看不到。
2.2 一条能直接用的备份语句和它的目录约定
先看单库备份的最小可用语句,我一般会先手动跑通它,确认权限和路径都没问题,再往作业里塞。
-- 单库完整备份,带压缩、校验和进度 BACKUP DATABASE [YourDB] TO DISK = N'\\FileServer\SQLBackup\YourDB\YourDB_FULL_20250101_020000.bak' WITH INIT, -- 覆盖同名文件,避免无限追加 COMPRESSION, -- 压缩备份,省空间也省 IO CHECKSUM, -- 写入校验和,恢复时可验证 STATS = 10, -- 每 10% 输出进度 NAME = N'YourDB-Full Backup'; -- 备份集名称,恢复时好认逻辑说明:TO DISK指向共享路径下的具体文件,文件名里带库名、类型和时间戳,这是后面做保留策略的基础。INIT配合每次生成新文件名,等于每次都是全新文件,不会出现一个文件里堆了几十个备份集、恢复时还要挑的情况。COMPRESSION在 CPU 富余、磁盘紧张的机器上收益明显,压缩比通常 3 到 5 倍。CHECKSUM会增加一点 CPU 开销,但换来的是恢复时的可验证性,生产库建议开。
参数怎么改:如果磁盘 IO 是瓶颈、CPU 很闲,压缩开着;反过来 CPU 已经打满,就关掉压缩。STATS的值可以调成 5 或 20,看你想多细的进度。备份文件扩展名用.bak是惯例,差异备份用.diff,日志备份用.trn,不是强制但方便人眼识别。
2.3 共享目录的权限配置和连通性验证
在写作业之前,必须确认服务账户能写到共享。步骤是这样:先查 SQL Server 服务用的是哪个账户,在「服务」里看 SQL Server (实例名) 的登录身份;然后到文件服务器上,把这个账户加到共享目录的「修改」权限里,同时 NTFS 权限也要给写。两步缺一不可,共享权限和 NTFS 权限是取交集的。
验证连通性别用资源管理器,用 SQL Server 自己跑一条:
-- 用 xp_fileexist 验证服务账户能否看到目标路径 EXEC master.dbo.xp_fileexist N'\\FileServer\SQLBackup\YourDB\';返回结果里File Exists为 1 说明路径可达。如果报错或返回 0,先查网络连通(服务账户所在机器能不能解析文件服务器名)、再查权限。这一步过了,备份语句基本就能跑通。
提示:如果共享在另一台机器上,备份流量会走网络。大库首次全备可能把网络打满,建议错峰,或者先备到本地再拷走。
3. 把备份脚本包装成可复用存储过程:动态库名、时间戳与保留策略
3.1 用游标遍历所有用户库并动态拼备份语句
单库跑通后,下一步是让脚本自动处理所有需要备份的库。思路是查sys.databases,过滤掉系统库和不需要备份的库,用游标逐个拼BACKUP语句并执行。这里必须用QUOTENAME包库名,防止库名里有特殊字符导致语句出错。
DECLARE @dbName SYSNAME; DECLARE @backupPath NVARCHAR(400); DECLARE @fileName NVARCHAR(500); DECLARE @sql NVARCHAR(MAX); DECLARE @ts VARCHAR(20) = CONVERT(VARCHAR(8), GETDATE(), 112) + '_' + REPLACE(CONVERT(VARCHAR(8), GETDATE(), 108), ':', ''); DECLARE db_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM sys.databases WHERE database_id > 4 -- 排除 master/model/msdb/tempdb AND state = 0 -- 只备在线库 AND is_read_only = 0; -- 只读库按需处理,这里先跳过 OPEN db_cursor; FETCH NEXT FROM db_cursor INTO @dbName; WHILE @@FETCH_STATUS = 0 BEGIN SET @backupPath = N'\\FileServer\SQLBackup\' + @dbName + N'\'; SET @fileName = @backupPath + @dbName + N'_FULL_' + @ts + N'.bak'; SET @sql = N'BACKUP DATABASE ' + QUOTENAME(@dbName) + N' TO DISK = N''' + @fileName + N'''' + N' WITH INIT, COMPRESSION, CHECKSUM, STATS = 10;'; BEGIN TRY EXEC sp_executesql @sql; END TRY BEGIN CATCH -- 单个库失败不影响其他库,错误写进日志表 INSERT INTO DBA_BackupLog(db_name, err_msg, log_time) VALUES (@dbName, ERROR_MESSAGE(), GETDATE()); END CATCH FETCH NEXT FROM db_cursor INTO @dbName; END CLOSE db_cursor; DEALLOCATE db_cursor;逻辑说明:时间戳用CONVERT拼成20250101_020000这种格式,排序友好、无非法字符。QUOTENAME给库名加方括号,避免库名带空格或横线时语法错误。TRY...CATCH保证一个库备份失败不会中断整个循环,错误落到日志表里,事后能查。database_id > 4是排除四个系统库的常用写法,但如果你有自定义的系统用途库,要单独判断。
参数怎么改:state = 0只备在线库,如果想把正在还原的库也排除,这个条件够了。只读库如果也要备,把is_read_only = 0去掉,但只读库通常不需要频繁全备,可以单独走低频策略。日志表DBA_BackupLog要提前建好,字段至少包含库名、错误信息、时间。
3.2 保留策略:按天清理还是按文件数清理
备份不能只增不减,否则共享盘迟早爆。保留策略有两种常见做法:按时间删(保留最近 N 天)和按文件数删(每个库保留最近 N 个文件)。按时间删更符合「保留 7 天」这种合规要求,按文件数删更适合备份频率不固定的场景。我一般用按时间删,配合文件名的日期部分做匹配。
-- 清理 7 天前的 .bak 文件,用 xp_delete_file 更安全 DECLARE @cutoff DATETIME = DATEADD(DAY, -7, GETDATE()); DECLARE @folder NVARCHAR(400) = N'\\FileServer\SQLBackup\YourDB\'; EXEC master.dbo.xp_delete_file 0, -- 0 表示备份文件 @folder, -- 目录 N'bak', -- 扩展名 @cutoff, -- 删除此时间之前的文件 1; -- 包含子目录逻辑说明:xp_delete_file是 SQL Server 内置的扩展存储过程,比用xp_cmdshell调del命令安全得多,它只删符合备份文件特征的文件,不会误删别的。第一个参数 0 代表备份文件,1 代表维护计划文件,别搞反。@cutoff是时间界限,早于它的文件被删。
参数怎么改:保留天数按你的 RPO 和磁盘容量定,7 天是常见起点。如果要做「每周全备 + 每天差异」,清理逻辑要分开,全备保留更久。xp_delete_file对 UNC 路径支持良好,但同样受服务账户权限约束,权限不够会静默失败,记得在日志里记录清理结果。
3.3 把存储过程和作业步骤串起来
脚本写成存储过程usp_AutoBackupAllDBs后,在 SQL Server 代理里建作业,步骤类型选 TSQL,命令就是EXEC usp_AutoBackupAllDBs;。作业计划按你的备份窗口设,比如每天凌晨 2 点。作业的「历史记录」要限制行数,不然日志表会无限涨。另外建议加一个失败通知,作业失败时发邮件或写事件日志,别等出事才发现备份早就挂了。
注意:代理作业的「所有者」最好是专门的运维账号,不要用个人账号。个人账号离职或改密码,作业会直接跑不起来。
4. 避坑与排查:共享备份最常见的五类翻车
4.1 作业报「操作系统错误 5(拒绝访问)」
现象:手动在 SSMS 里跑备份语句成功,作业一跑就报拒绝访问。原因几乎都是权限主体搞错了——你手动跑用的是你的 Windows 账号,作业跑用的是 SQL Server 服务账户或代理账户。解决:确认服务账户,把它加到共享的共享权限和 NTFS 权限里,两步都给「修改」。验证用xp_fileexist以服务身份跑,别用资源管理器。
4.2 备份文件越来越大,磁盘被撑爆
现象:共享盘空间告急,一看全是.bak。原因通常是用了NOINIT往同一个文件追加,或者保留策略没生效。解决:备份语句统一用INIT加时间戳文件名,清理任务用xp_delete_file按天删,并且把清理步骤放在备份步骤之后、同一个作业里。清理失败要能报警,否则就是定时炸弹。
4.3 备份成功但恢复时报「校验失败」
现象:备份作业显示成功,真恢复时提示校验和错误或页损坏。原因是备份时没开CHECKSUM,坏页被原样写进备份。解决:备份语句加CHECKSUM,并且定期做RESTORE VERIFYONLY验证备份可读。验证语句很简单:
RESTORE VERIFYONLY FROM DISK = N'\\FileServer\SQLBackup\YourDB\YourDB_FULL_20250101_020000.bak' WITH CHECKSUM;这条不实际恢复,只校验备份集完整性和校验和,几分钟就能跑完,建议纳入日常巡检。
4.4 网络抖动导致备份中断,作业却显示成功
现象:备份到一半网络断了,作业历史里却是成功。原因是BACKUP语句在某些错误下不抛异常,或者TRY...CATCH没覆盖到。解决:备份后检查文件大小是否合理,或者在脚本里加一步RESTORE VERIFYONLY,验证不过就写日志并让作业失败。另外共享路径尽量走稳定的内网,别跨广域网。
4.5 差异备份基准被临时全备打乱
现象:做了差异备份策略后,某次临时手动全备导致后续差异备份变得巨大或恢复链断裂。原因是临时全备没加COPY_ONLY,它重置了差异基准。解决:所有非计划内的临时备份一律加COPY_ONLY,计划内的全备才参与差异链。这个坑很隐蔽,等发现时恢复链已经乱了。
5. 进阶:让备份可观测、可验证、可迁移
5.1 用日志表把每次备份变成可查询的数据
光靠作业历史不够,作业历史会被截断,也不方便做统计。我习惯建一张备份日志表,每次备份前后各写一条记录,包含库名、开始时间、结束时间、文件路径、文件大小、是否成功。这样能直接查「最近 7 天哪些库没备份成功」「哪个库备份耗时突然变长」。文件大小可以用xp_fileexist配合sys.dm_os_file_exists拿不到,实际做法是备份后用xp_cmdshell调dir或者用 PowerShell 取,但xp_cmdshell有安全顾虑,更稳的是在备份语句后用RESTORE HEADERONLY读备份集信息,里面包含备份大小和时间。
-- 读取备份集元数据,写入日志表 RESTORE HEADERONLY FROM DISK = N'\\FileServer\SQLBackup\YourDB\YourDB_FULL_20250101_020000.bak';这条返回的结果集里有BackupSize、BackupStartDate、BackupFinishDate,把它们插进日志表,就有了可查询的备份档案。比解析文件名可靠得多。
5.2 迁移到新服务器时怎么快速复用
整套方案的可迁移性是它相对维护计划的最大优势。迁移时只需要改三个地方:共享路径变量、服务账户权限、作业计划时间。存储过程和日志表结构用脚本导出,在新实例上重建即可。我一般会把整个方案写成一个部署脚本,包含建日志表、建存储过程、建作业三部分,换环境时跑一遍就行。作业的创建可以用sp_add_job、sp_add_jobstep、sp_add_jobschedule这些系统存储过程,也可以直接在 SSMS 里建好再脚本化。
5.3 一个我踩过的坑:别把备份和清理放同一个 TRY 块
早期我把备份和清理写在同一个TRY...CATCH里,结果备份失败时清理照样跑,把仅有的几个旧备份也删了,差点酿成事故。后来改成备份和清理完全独立,清理只在备份成功后才执行,并且清理本身也有独立的错误处理。这个习惯救过我一次——有回新加的库路径权限没配好,备份全失败,但清理因为没触发,旧备份完好无损。备份方案的第一原则是「宁可留着旧的,也别删了新的」,希望帮到你。
本文还有配套的精品资源,点击获取