
简介这份源码面向使用 C# 与 SQL Server 的开发者尤其是需要为业务系统搭建数据库备份与作业调度模块的中级 .NET 工程师。它基于 Visual Studio 2010、SQL Server 2008 与 .NET 4.0 开发提供自动备份、手动备份、备份文件上传 FTP、自动与手动处理作业、参数编辑及日志清空等完整功能可直接参考或二次开发。压缩包共 87 个文件约 1.97MB以 29 个 cs 源码文件为核心配合 resx、resources 资源文件、ico 与 png 图标、dll 依赖库、config 与 xml 配置、sql 脚本及 exe 可执行程序结构覆盖界面窗体、数据访问、FTP 与备份辅助类等模块。目前已有 434 人学习下载。读者可从中获取一套可运行的备份工具工程理解备份线程调度、作业处理与 FTP 上传的实现思路并借助配置与脚本文件快速完成本地部署与调试。1. 从一次误删事故说起这套 C# 备份工具源码到底能干什么凌晨两点运维群里有人发了一张截图生产库被一条没加 WHERE 的 DELETE 扫了一遍三十多万行数据没了。更扎心的是这台 SQL Server 的维护计划里只配了完整备份最近一次可用还原点是前一天晚上十点——也就是说四个小时的增量数据只能靠手工补。那次之后我翻了不少现成的备份方案要么是命令行脚本散落一地没人维护要么是商业软件按实例收费贵得离谱最后落到一个自己可控的方向用 C# 写一个带界面的 SQL Server 备份工具把完整备份、差异备份、事务日志备份和还原入口都收在一个 exe 里。这套 C# SQL Server 数据库备份工具源码核心就是用 .NET 的Microsoft.Data.SqlClient或System.Data.SqlClient连上实例通过 T-SQL 的BACKUP DATABASE/BACKUP LOG语句驱动备份再用RESTORE系列语句做还原界面层用 WinForms 或 WPF 把实例列表、库列表、备份路径、计划任务串起来。它解决的不是备份有多难这个问题——BACKUP DATABASE本身一行就够——而是把多实例、多库、定时、日志清理、还原验证这些琐碎事固化成一个可维护的工程。适合谁手上管着几台到几十台 SQL Server、又不想被商业备份软件绑定的 .NET 开发者或运维也适合正在学 C# 上位机、想找一个真实数据库交互项目练手的人。下面我按能跑起来 → 能改明白 → 不踩坑的顺序拆一遍。2. 环境准备与连接层把 SqlConnection 和实例枚举跑通2.1 先确认你面对的是哪种 SQL Server 部署形态动手之前得先搞清楚目标实例长什么样因为这直接决定连接字符串怎么写、备份路径往哪落。常见三种形态默认实例MSSQLSERVER端口 1433、命名实例如SQLEXPRESS走动态端口或 SQL Browser 服务、以及多实例共存。命名实例最容易翻车——很多人连接串里只写Serverlocalhost结果连到了默认实例备份出来的库根本不是想要的那个。枚举本机实例有个取巧办法读注册表SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL或者直接调SqlDataSourceEnumerator.Instance.GetDataSources()。后者能扫局域网但依赖 SQL Browser 服务UDP 1434服务没开就扫不到这是血泪经验。我一般两种都留着本机走注册表远程走枚举加手工输入兜底。连接字符串的关键参数就几个别堆太多// 连接字符串构造按部署形态区分 public static string BuildConnectionString(string server, string database, bool integratedSecurity, string user , string pwd ) { var builder new SqlConnectionStringBuilder { DataSource server, // 形如 192.168.1.10,1433 或 localhost\\SQLEXPRESS InitialCatalog database, // 备份场景常填 master因为要跨库操作 IntegratedSecurity integratedSecurity, ConnectTimeout 15, // 默认 15 秒跨网段可调到 30 ApplicationName DbBackupTool // 便于在 SQL Server 活动监视器里定位来源 }; if (!integratedSecurity) { builder.UserID user; builder.Password pwd; } return builder.ConnectionString; }逻辑说明DataSource里命名实例必须用双反斜杠转义端口用逗号分隔而不是冒号这是 SQL Server 特有的写法写错了报找不到服务器。InitialCatalog在备份工具里通常填master因为BACKUP DATABASE语句本身要指定目标库名连接库填 master 权限更干净。ConnectTimeout默认 15 秒跨机房或负载高的实例建议 30 秒否则界面会频繁弹超时。2.2 用 sys.databases 拉库列表别用 sp_helpdb拿到连接后第一件事是列出可备份的库。很多人习惯sp_helpdb但它返回的字段杂、还要解析文本。更稳的是查系统视图-- 只列出在线、可备份的用户库 SELECT name, database_id, state_desc, recovery_model_desc, compatibility_level FROM sys.databases WHERE database_id 4 -- 排除 master/model/msdb/tempdb AND state 0 -- 0 ONLINE AND is_read_only 0 -- 只读库备份意义有限按需放开 ORDER BY name;recovery_model_desc这个字段很关键只有FULL或BULK_LOGGED恢复模式的库才支持事务日志备份SIMPLE模式下你调BACKUP LOG会直接报错。工具界面里应该根据这个字段动态禁用日志备份按钮而不是等用户点了再抛异常。state_desc用来过滤掉RECOVERING、RESTORING这些非在线状态否则备份语句会卡住。2.3 权限最小化别用 sa 跑工具连接层跑通后马上要面对权限问题。用sa当然能跑但这是给自己埋雷。备份操作需要的权限其实很明确db_backupoperator角色能备份单个库跨库备份和日志备份需要sysadmin或db_owner加BACKUP DATABASE权限。还原更麻烦RESTORE要求dbcreator或sysadmin。我的做法是给工具单独建一个登录名只授db_backupoperator加dbcreator够备份和还原用不给sysadmin。如果实例上有 Always On 或镜像备份语句还要加COPY_ONLY避免打断日志链这个后面讲。权限这块没有捷径先在测试实例上把最小权限试出来再上生产。3. 备份核心逻辑BACKUP DATABASE 的参数怎么设才不翻车3.1 完整备份、差异备份、日志备份的语句差异三种备份的 T-SQL 骨架不一样参数含义也不同混用会出大问题。完整备份是基线差异备份依赖最近的完整备份日志备份依赖完整备份且要求 FULL 恢复模式。下面是我在工具里用的三条语句模板// 三种备份类型的 T-SQL 生成 public static string BuildBackupSql(string dbName, string backupType, string filePath, bool copyOnly false) { // WITH 选项COMPRESSION 省空间但吃 CPUCHECKSUM 校验页完整性STATS 报进度 string withClause WITH COMPRESSION, CHECKSUM, STATS 10; if (copyOnly) withClause , COPY_ONLY; // Always On 从库备份必须加 switch (backupType) { case FULL: return $BACKUP DATABASE [{dbName}] TO DISK N{filePath} {withClause}; case DIFF: return $BACKUP DATABASE [{dbName}] TO DISK N{filePath} WITH DIFFERENTIAL, COMPRESSION, CHECKSUM, STATS 10; case LOG: return $BACKUP LOG [{dbName}] TO DISK N{filePath} {withClause}; default: throw new ArgumentException(未知备份类型); } }逻辑说明COMPRESSION在 SQL Server 2008 及以上默认可用压缩比通常 3 到 5 倍但会占 CPUCPU 紧张的实例可以关掉。CHECKSUM会在备份时校验页校验和能提前发现坏页代价是备份时间增加约 5% 到 10%我建议开着。STATS 10让 SQL Server 每完成 10% 回报一次进度工具界面靠这个刷新进度条不设的话用户只能干等。COPY_ONLY是 Always On 可用性组从库备份的必备选项不加会破坏日志链主库还原时直接失败。3.2 备份文件命名与目录规划文件名乱起是还原时的噩梦。我见过有人用backup1.bak、backup2.bak这种命名三个月后没人知道哪个对应哪个库。工具里应该强制一套命名规则// 备份文件名库名_类型_时间戳.bak/.trn public static string BuildBackupFileName(string dbName, string backupType, DateTime now) { string ext backupType LOG ? .trn : .bak; string typeTag backupType switch { FULL FULL, DIFF DIFF, LOG LOG, _ UNK }; // 时间戳精确到秒避免同秒覆盖 return ${dbName}_{typeTag}_{now:yyyyMMdd_HHmmss}{ext}; }目录规划上完整备份、差异备份、日志备份建议分目录存放因为保留策略不同完整备份留 7 到 14 天差异备份留 3 天日志备份留 48 小时。混在一个目录里清理脚本很难写。另外备份目录不要和数据库数据文件放同一个物理盘盘挂了数据和备份一起没这是最基本的容灾常识。3.3 用 SqlCommand 执行并捕获进度执行备份语句时SqlCommand的ExecuteNonQuery会阻塞到备份完成长备份可能几十分钟。工具里要么放后台线程要么用异步。进度信息通过InfoMessage事件拿// 执行备份并实时接收 STATS 进度 public static async Task ExecuteBackupAsync(string connStr, string sql, Actionint onProgress) { using var conn new SqlConnection(connStr); conn.InfoMessage (s, e) { // STATS 输出形如 10 percent processed. foreach (SqlError msg in e.Errors) { var m System.Text.RegularExpressions.Regex.Match(msg.Message, (\d)\spercent); if (m.Success int.TryParse(m.Groups[1].Value, out int pct)) onProgress?.Invoke(pct); } }; await conn.OpenAsync(); using var cmd new SqlCommand(sql, conn) { CommandTimeout 0 }; // 0 不限时 await cmd.ExecuteNonQueryAsync(); }逻辑说明CommandTimeout 0表示不限时备份大库时默认 30 秒超时肯定不够必须设 0 或设成很大。InfoMessage事件是 SQL Server 回传PRINT和RAISERROR严重级别 10 以下的通道STATS的进度就走这里。正则匹配percent关键字提取百分比比解析整段文本稳。注意InfoMessage在连接级别触发多个命令共用连接时要注意区分。3.4 备份后的校验RESTORE VERIFYONLY备份文件写完不代表能用。磁盘坏道、网络传输截断都可能让 .bak 文件损坏。工具里应该在备份完成后自动跑一次校验-- 校验备份文件可读且结构完整不实际还原 RESTORE VERIFYONLY FROM DISK ND:\Backup\MyDb_FULL_20250101_020000.bak WITH CHECKSUM;VERIFYONLY只读备份头、校验页校验和不写数据文件速度快适合每次备份后自动跑。如果备份时没加CHECKSUM这里的WITH CHECKSUM会报错所以两者要配套。校验失败说明备份文件不可用工具应该立刻告警而不是等真出事才发现。4. 还原与计划任务把 RTO 压到可接受范围4.1 还原顺序完整 → 差异 → 日志一步都不能少还原是备份的逆过程但顺序和选项更讲究。完整备份还原用RESTORE DATABASE ... WITH NORECOVERY差异和日志继续用NORECOVERY最后一条日志用WITH RECOVERY让库上线。中间任何一步用了RECOVERY后续备份就接不上了。-- 第一步还原完整备份保持 NORECOVERY 以便继续 RESTORE DATABASE [MyDb] FROM DISK ND:\Backup\MyDb_FULL_20250101_020000.bak WITH NORECOVERY, REPLACE, STATS 10; -- 第二步还原差异备份 RESTORE DATABASE [MyDb] FROM DISK ND:\Backup\MyDb_DIFF_20250101_140000.bak WITH NORECOVERY, STATS 10; -- 第三步还原日志备份可多条最后一条用 RECOVERY RESTORE LOG [MyDb] FROM DISK ND:\Backup\MyDb_LOG_20250101_160000.trn WITH NORECOVERY, STATS 10; RESTORE LOG [MyDb] FROM DISK ND:\Backup\MyDb_LOG_20250101_180000.trn WITH RECOVERY, STATS 10;REPLACE选项在目标库已存在时覆盖测试环境常用生产环境慎用——它会直接覆盖现有库没有二次确认就是灾难。NORECOVERY和RECOVERY的区别是还原过程中最容易搞混的点前者让库处于正在还原状态可以继续接后续备份后者让库上线可读但不能再接备份。工具里应该把还原步骤做成向导每步显示当前状态避免用户手动拼错。4.2 时间点还原把 RTO 压到分钟级如果日志备份足够密比如每 15 分钟一次可以做时间点还原把库恢复到某个精确时刻。这对误删事故特别有用——恢复到 DELETE 执行前一秒。-- 还原到指定时间点STOPAT 精确到毫秒 RESTORE DATABASE [MyDb] FROM DISK ND:\Backup\MyDb_FULL_20250101_020000.bak WITH NORECOVERY, REPLACE; RESTORE LOG [MyDb] FROM DISK ND:\Backup\MyDb_LOG_20250101_160000.trn WITH NORECOVERY, STOPAT N2025-01-01T15:59:59; RESTORE DATABASE [MyDb] WITH RECOVERY;STOPAT的时间点必须落在某条日志备份覆盖的时间范围内超出范围会报错。工具里应该先查msdb.dbo.backupset表列出可用的日志备份时间范围让用户在这个范围内选而不是随便填。STOPAT和STOPATMARK的区别是前者按时间、后者按标记日常用STOPAT就够。4.3 计划任务用 SQL Server 代理还是 Windows 任务计划定时备份有两条路SQL Server 代理作业或者 Windows 任务计划调工具的命令行模式。代理作业的好处是 SQL Server 自己管调度、有历史记录、能发邮件告警坏处是依赖代理服务且作业脚本要单独维护。Windows 任务计划的好处是工具自己控制一切坏处是调度和告警要自己实现。我一般推荐代理作业因为备份这种基础设施级任务交给数据库自己的调度器更稳。工具可以生成作业脚本-- 创建代理作业每天凌晨 2 点完整备份 USE msdb; EXEC dbo.sp_add_job job_name NBackup_MyDb_Full; EXEC dbo.sp_add_jobstep job_name NBackup_MyDb_Full, step_name NFullBackup, subsystem NTSQL, command NBACKUP DATABASE [MyDb] TO DISK ND:\Backup\MyDb_FULL_$(ESCAPE_SQUOTE(STRTDTIME)).bak WITH COMPRESSION, CHECKSUM, STATS 10; EXEC dbo.sp_add_schedule schedule_name NDaily2AM, freq_type 4, freq_interval 1, active_start_time 20000; EXEC dbo.sp_attach_schedule job_name NBackup_MyDb_Full, schedule_name NDaily2AM; EXEC dbo.sp_add_jobserver job_name NBackup_MyDb_Full;$(ESCAPE_SQUOTE(STRTDTIME))是代理作业的令牌会替换成作业开始时间用来生成唯一文件名。freq_type 4表示每天active_start_time 20000表示 2:00:00格式 HHMMSS。这套脚本工具可以一键生成比让用户手点界面可靠。4.4 保留策略与日志清理备份文件不清理磁盘迟早爆。保留策略要按备份类型分开完整备份留 14 天差异留 3 天日志留 48 小时。清理脚本用 T-SQL 查msdb.dbo.backupset找到过期备份记录再删文件-- 查询 14 天前的完整备份文件路径 SELECT bmf.physical_device_name, bs.backup_finish_date FROM msdb.dbo.backupset bs JOIN msdb.dbo.backupmediafamily bmf ON bs.media_set_id bmf.media_set_id WHERE bs.type D -- D完整, I差异, L日志 AND bs.backup_finish_date DATEADD(DAY, -14, GETDATE()) ORDER BY bs.backup_finish_date;拿到路径后用 C# 的File.Delete删文件再调sp_delete_backuphistory清理 msdb 历史记录。注意sp_delete_backuphistory删的是历史记录不是文件两者要分开做。清理脚本本身也要有日志删了什么、删了多少出问题能追溯。5. 避坑与排查这几处翻车我替你踩过了5.1 备份文件写到网络共享报操作系统错误 5拒绝访问现象BACKUP DATABASE ... TO DISK \\NAS\backup\x.bak报操作系统错误 5本地路径正常。原因SQL Server 服务账户是NT Service\MSSQLSERVER或域账户它访问网络共享用的身份和你登录 Windows 的身份不是一回事。你资源管理器能打开共享不代表 SQL Server 服务账户能写。解决给 SQL Server 服务账户在共享目录上授写权限或者改用本地路径备份后再用工具搬走。另外 UNC 路径要用双反斜杠且 SQL Server 服务账户要有作为服务登录权限。这个坑几乎每个用网络备份的人都会踩一次。5.2 日志备份报无法备份日志因为没有任何当前数据库备份现象对 FULL 恢复模式的库直接执行BACKUP LOG报错 4214。原因日志备份依赖一条完整备份作为日志链起点。新库或刚切到 FULL 恢复模式还没做过完整备份时日志链没建立。解决先做一次完整备份再做日志备份。工具里应该在日志备份前检查msdb.dbo.backupset里有没有该库的完整备份记录没有就提示先做完整备份。切恢复模式后也要提醒用户补一次完整备份。5.3 差异备份越来越大接近完整备份体积现象差异备份文件从几十 MB 涨到几个 GB和完整备份差不多大。原因差异备份记录的是自上次完整备份以来所有变化的页。如果中间做过索引重建、大批量更新变化页会非常多差异备份自然膨胀。另外如果完整备份失败没成功差异备份会一直基于更早的完整备份累积。解决定期比如每周做一次完整备份重置差异基线大批量操作后手动补一次完整备份。工具里可以监控差异备份体积超过完整备份 50% 就告警。5.4 还原时目标库正在被使用报数据库正在使用现象RESTORE DATABASE报错 3101说数据库正在使用无法获得独占访问。原因有活动连接连着目标库还原需要独占访问。解决还原前把目标库切到单用户模式或离线ALTER DATABASE [MyDb] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; -- 执行还原 ALTER DATABASE [MyDb] SET MULTI_USER;ROLLBACK IMMEDIATE会强制回滚活动事务并断开连接生产环境用之前要确认没有关键业务在跑。工具里应该把这一步做成还原向导的必选前置而不是让用户自己记得。5.5 备份到同一物理盘盘挂全没现象数据库数据文件和备份文件都在 D 盘D 盘物理损坏数据和备份一起丢。原因备份的意义是异地或异盘容灾同盘备份等于没备份。解决备份目录必须和数据文件分盘最好分机器。工具里可以在配置界面强制校验备份路径和数据文件路径不在同一盘符不满足就警告。这条没有技术含量但每年都有团队栽在上面。6. 进阶把备份验证和告警做成闭环前面讲的都是备份能跑但备份工具真正的价值在于出事时敢用。我现在的习惯是每周做一次自动还原演练把最近一次完整备份加差异加日志还原到一个临时库跑几条校验查询确认数据完整后删掉临时库。这套流程用工具串起来比人工点界面可靠得多。还原演练的核心是自动挑备份链。给定一个目标时间点从msdb.dbo.backupset里找最近一次完整备份、其后的差异备份、以及到目标时间点为止的所有日志备份按顺序拼成还原脚本// 自动挑选还原链完整 - 差异 - 日志序列 public static Liststring BuildRestoreChain(string dbName, DateTime targetTime) { var chain new Liststring(); // 1. 找 targetTime 之前最近一次完整备份 // 2. 找该完整备份之后、targetTime 之前最近一次差异备份 // 3. 找差异备份之后到 targetTime 之间的所有日志备份 // 具体查询略核心是 backupset 表的 type 和 backup_finish_date 字段 return chain; }这段逻辑的关键是时间边界完整备份要选backup_finish_date targetTime里最新的差异备份要选在完整备份之后且 targetTime里最新的日志备份要覆盖从差异备份到 targetTime 的整个区间。任何一段缺失还原链就断了工具应该明确告诉用户断在哪而不是拼一个跑不通的脚本。验证环节我一般跑三类查询行数对比关键表行数和生产库接近、最新时间戳业务表的最大创建时间在目标时间点附近、以及DBCC CHECKDB的快速版。前两个快几秒出结果CHECKDB慢可以放到周末演练时跑。三类都过了这次备份才算可用。告警这块工具里我留了三个触发点备份失败立即告警、备份成功但VERIFYONLY失败告警、连续两次备份间隔超过阈值告警。告警通道用 SMTP 发邮件最省事SmtpClient几行代码搞定。别小看连续两次间隔超阈值这条——它抓的是备份任务悄悄没跑这种最隐蔽的故障比备份失败还危险因为没人知道备份已经停了。还有个细节备份文件的权限。备份文件里是完整数据权限不能太松。工具生成的 .bak 文件默认继承目录权限如果目录是 Everyone 可读等于数据裸奔。我一般把备份目录权限收紧到只有 SQL Server 服务账户和管理员组可读写工具里加一条权限检查提示。从那以后我每次上线新的备份策略都强制走一遍备份 → VERIFYONLY → 还原到临时库 → 跑校验查询 → 删临时库的完整闭环任何一步不过就不算完成。这套流程多花二十分钟但换来的是真出事时敢按下还原键的底气。希望帮到你。本文还有配套的精品资源点击获取