65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的 SQL Server 数据库备份实用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (8投票s)

2018年6月5日

CPOL

4分钟阅读

viewsIcon

19529

downloadIcon

16

用于按计划创建 SQL Server 数据库备份的简单实用程序

摘要

本文介绍了一个简单的实用程序,用于按计划创建 SQL Server 数据库的备份。设置指定了您要备份的数据库名称以及保存目录。旧的数据库会自动删除。

引言

如今,很难想象一个不以某种方式使用数据库的现代重要项目。Web 应用程序、数据收集系统、支付系统、工业系统——所有这些都需要组织和使用大量数据。

我最近在我的项目中使用 SQL Server 数据库。许多技术都内置了对这些数据库的支持,例如 ASP.NET MVC、Web API、Entity Framework 等。如果您从模板开始一个项目,通常还会创建一个简单的 SQL Server 演示数据库。创建新数据库或编辑现有 SQL Server 数据库模式很简单。为此,Visual Studio 提供了内置支持。此外,SQL Server 还包含内置的 Management Studio IDE。

构建

在我启动了几个使用 SQL Server 的自定义软件项目后,我遇到了定期备份数据库的需求。为什么要备份数据库?我们都面临着硬盘不可靠、勒索软件病毒、软件故障和其他问题。手头有一个最新的备份总比没有好。要自动按计划创建备份,我在我的软件解决方案中包含了一个小型项目,其任务就是创建备份副本。

拥有一个独立的项目而不是在主应用程序中启用备份功能的好处是,您不必每次都将唯一的备份功能插入新项目并考虑如何使用它。

实现

备份项目非常简单,它基于 C# 控制台应用程序。该应用程序设计用于按计划定期启动。

考虑应用程序代码,它并不复杂

using System;
using System.Data.SqlClient;
using System.IO;
using System.Threading;
using Ionic.Zip;

namespace SimpleDBBackup
{
    class Program
    {
        /* Local SQL Server connecton string */
        static string connectionString = "Server=localhost;Integrated Security=True";

        //Optional:connect using credentials
        //static string connectionString = "Server=localhost;user id=user2018;password=MYDBPASSWORD;";

        /* Database names to backup */
        static string[] saDatabases = new string[] { "shop", "frontend", "accounting" };

        /* Backup directory. Please note: Files older than DeletionDays old will be deleted automatically */
        static string backupDir = @"C:\DB_Backups";

        /* Delete backups older than DeletionDays. Set this to 0 to never delete backups */
        static int DeletionDays = 10;



        static Mutex mutex = new Mutex(true, "Global\\SimpleDBBackupMutex");
        static void Main(string[] args)
        {
            // allow only single instance of the app
            if (!mutex.WaitOne(TimeSpan.Zero, true))
            {
                Console.WriteLine("Program already running!");
                return;
            }

            if (DeletionDays > 0)
                DeleteOldBackups();

            DateTime dtNow = DateTime.Now;

            try
            {
                using (SqlConnection sqlConnection = new SqlConnection(connectionString))
                {
                    sqlConnection.Open();

                    foreach (string dbName in saDatabases)
                    {
                        string backupFileNameWithoutExt = String.Format("{0}\\{1}_{2:yyyy-MM-dd_hh-mm-ss-tt}", backupDir, dbName, dtNow);
                        string backupFileNameWithExt = String.Format("{0}.bak", backupFileNameWithoutExt);
                        string zipFileName = String.Format("{0}.zip", backupFileNameWithoutExt);

                        string cmdText = string.Format("BACKUP DATABASE {0}\r\nTO DISK = '{1}'", dbName, backupFileNameWithExt);

                        using (SqlCommand sqlCommand = new SqlCommand(cmdText, sqlConnection))
                        {
                            sqlCommand.CommandTimeout = 0;
                            sqlCommand.ExecuteNonQuery();
                        }

                        using (ZipFile zip = new ZipFile())
                        {
                            zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
                            zip.AddFile(backupFileNameWithExt);
                            zip.Save(zipFileName);
                        }
                        
                        File.Delete(backupFileNameWithExt);
                    }

                    sqlConnection.Close();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            mutex.ReleaseMutex();
        }

        static void DeleteOldBackups()
        {
            try
            {
                string[] files = Directory.GetFiles(backupDir);

                foreach (string file in files)
                {
                    FileInfo fi = new FileInfo(file);
                    if (fi.CreationTime < DateTime.Now.AddDays(-DeletionDays))
                        fi.Delete();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}


应用程序由几个主要块组成

  1. 设置块
  2. 主函数,在此创建实际的备份
  3. DeleteOldBackups 函数,在此删除旧的备份

详细考虑这些块

设置块

        /* Local SQL Server connecton string */
        static string connectionString = "Server=localhost;Integrated Security=True";

        //Optional:connect using credentials
        //static string connectionString = "Server=localhost;user id=user2018;password=MYDBPASSWORD;";

在这里,您指定数据库的连接字符串。在最简单的情况下,如果您使用具有默认设置的本地服务器,第一行就足够了。如果您想使用登录名和密码连接到服务器,请取消注释并修改第二行。

        /* Database names to backup */
        static string[] saDatabases = new string[] { "shop", "frontend", "accounting" };

这是一个包含服务器上数据库名称的数组,您想要为其创建备份。如果您需要备份一个数据库,您可以这样做

        static string[] saDatabases = new string[] { "shop" };

 

        /* Backup directory. Please note: Files older than DeletionDays old will be deleted automatically */
        static string backupDir = @"C:\DB_Backups";

这是用于存储备份的目录名称。**请注意,此目录仅应用于存储备份,而不用于任何其他目的。为什么?因为在备份副本的某个轮换期之后,此目录中的文件将被删除**(如下所示)

        /* Delete backups older than DeletionDays. Set this to 0 to never delete backups */
        static int DeletionDays = 10;

这是删除旧数据库的天数。

主函数

        static Mutex mutex = new Mutex(true, "Global\\SimpleDBBackupMutex");
        static void Main(string[] args)
        {
            // allow only single instance of the app
            if (!mutex.WaitOne(TimeSpan.Zero, true))
            {
                Console.WriteLine("Program already running!");
                return;
            }

在 Main 函数的开头,我安装了 Mutex(互斥)对象。此对象只能存在一个实例。在此程序中,我使用 Mutex 来防止程序第二次启动。使用 Mutex 对象,您可以进行许多有趣而迷人的进程和线程交互活动。

Main 函数中的下一个有趣块是连接到数据库和执行备份命令

                using (SqlConnection sqlConnection = new SqlConnection(connectionString))
                {
                    sqlConnection.Open();

                  ...

                        using (SqlCommand sqlCommand = new SqlCommand(cmdText, sqlConnection))
                        {
                            sqlCommand.CommandTimeout = 0;
                            sqlCommand.ExecuteNonQuery();
                        }

                      ...

                    sqlConnection.Close();
                }

备份文件本身的名称由当前日期和时间组成

string backupFileNameWithoutExt = String.Format("{0}\\{1}_{2:yyyy-MM-dd_hh-mm-ss-tt}", backupDir, dbName, dtNow);

备份副本使用不频繁,因此最好进行压缩以节省磁盘空间。为此,我们建议使用 DotNetZip 包

                        using (ZipFile zip = new ZipFile())
                        {
                            zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
                            zip.AddFile(backupFileNameWithExt);
                            zip.Save(zipFileName);
                        }

 

不要忘记关闭连接、删除不必要的文件并处理异常!

DeleteOldBackups 函数

这里一切都很简单:我们获取一个带有创建日期属性的文件列表,然后删除所有创建日期早于当前日期减去指定天数的文件

        static void DeleteOldBackups()
        {

            try
            {
                string[] files = Directory.GetFiles(backupDir);
                foreach (string file in files)
                {
                    FileInfo fi = new FileInfo(file);
                    if (fi.CreationTime < DateTime.Now.AddDays(-DeletionDays))
                        fi.Delete();
                }
            }

            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

如何使用程序

程序配置并编译为 Release 版本后,有必要将其与库一起复制到一个单独的目录中,例如 C:\SimpleDbBackup,同时创建一个用于存储数据库的目录(在本例中为 C:\DB_Backups)

现在您需要使用 Windows 任务计划程序创建一个定期任务,以便它每天或每天两次按计划运行,随您喜好。我建议您现在手动创建一个任务。也许将来我会在主程序代码中添加创建计划任务的功能,但目前,我建议您手动完成。

我将非常乐意接受任何评论和建议。如果您想自己为程序添加任何缺失的功能。欢迎克隆 github repo

祝您好运!

版本历史

2018-06-05 发布第一个版本

© . All rights reserved.