【问题标题】:Wait for connections to close before restoring SQL Server database在还原 SQL Server 数据库之前等待连接关闭
【发布时间】:2009-06-25 18:13:54
【问题描述】:

我有一个使用两个数据库的 Web 应用程序。 DB1 用户执行他们的 CRUD(创建、读取、更新、删除)操作。数据库 DB2 是我用于报告目的的不同服务器上的只读数据库。每小时我的 DB1 都会保存事务日志,在 DB2 上我有一个工作可以在 DB2 上恢复它们​​以保持它的最新状态。

我面临的问题是,如果有用户在 DB2 上运行报告(经常发生),当我获得恢复数据库的独占访问权限时,他们会与 sql 服务器断开连接。恢复每个日志所需的时间范围在 1-4 分钟之间。

我如何实现让我们称之为 wait-n-restore 功能,我的工作在将数据库切换为独占访问并恢复日志之前等待用户查询完成?

我的两台机器都运行 SQL Server 2008 64 位标准版

【问题讨论】:

    标签: sql-server sql-server-2008 replication


    【解决方案1】:

    我的问题的很大一部分是使用池连接 - 在这种情况下,即使没有执行任何报告,连接也保持活动状态。我修改了我的连接字符串,将连接池设置为 false,并在循环中检查打开的用户连接到您的报告数据库,直到值为 0。幸运的是,我不必创建触发器来反弹用户。

    【讨论】:

      【解决方案2】:

      您可能有一个 alter Database Setting Single User 或 Admin 模式,其中包含“WITH IMMEDIATE ROLLBACK”。这就是把用户踢出去的原因。去掉那个子句,它会等待他们离开(但也不会阻止新的进来)。

      RE:您的 Kill sProc:您可能需要查看“立即回滚”选项。

      至于防止新连接:我过去所做的是禁用应用程序用户的登录(服务器主体),等待最多 10 分钟,每分钟检查一下是否每个人都出去了。之后,我执行 ALTER DATABASE...WITH IMMEDIATE ROLLBACK,然后执行任何需要执行的 OPS 功能。

      我很幸运,登录始终是一次性应用程序用户登录(即仅用于此目的的 SQL 登录)如果您不能这样做,那么我目前唯一能想到的另一件事就是是拒绝对 DB 用户(数据库主体)的 CONNECT 权限。然后稍后撤销拒绝。我从来没有这样做过,但它应该是这样的:

      DENY CONNECT TO SomeDBUserName;
      

      【讨论】:

      • 我实际上使用了一个将所有人踢出的存储过程: ALTER PROCEDURE [dbo].[USP_GETEXCLUSIVE] @dbname varchar(100) AS DECLARE @KILL_ID int DECLARE @QUERY VARCHAR(320) DECLARE GETEXCLUSIVE_CURSOR CURSOR FOR从 SYSPROCESSES A 中选择 A.SPID 在 A.DBID=B.DBID 上加入 SYSDATABASES B,其中 B.NAME=@DBNAME 打开 GETEXCLUSIVE_CURSOR 从 GETEXCLUSIVE_CURSOR 获取下一个到 @KILL_ID WHILE(@@FETCH_STATUS =0) BEGIN SET @QUERY = 'KILL' + CONVERT(VARCHAR,@KILL_ID) EXEC (@QUERY) 从 GETEXCLUSIVE_CURSOR 获取 NEXT 到 @KILL_ID END CLOSE GETEXCLUSIVE_CURSOR DEALLOCATE GETEXCLUSIVE_CURSOR
      • 最大的问题是如何在我等待的时候不让数据库接受新的连接。
      • 我认为这是他想要避免的。在 Biztalk 中,有一个 Shutdown 模式允许您关闭新连接的服务,但允许现有进程完成,然后在它们空闲时立即将它们踢出。我认为他想要 SQL Server 中的等效功能。
      • rwmnau:我所描述的(拒绝连接)应该提供这一点。当前的连接应该没问题,只是不允许任何新的连接。
      【解决方案3】:

      我假设您的恢复是作为一项工作进行的。那么你需要的是一个登录触发器。以下是创建登录触发器的方法:

      登录触发器

      建立会话时会触发登录触发器。此时会引发 LOGON 事件。

      登录触发器的生命周期非常简单:用户连接到 Sql Server,触发器触发,隐式事务打开,然后由您决定!如果出于任何原因想要拒绝登录 Sql Server 的尝试,只需发出 ROLLBACK 语句即可。

      这是一个示例登录触发器:

      USE master;
      GO
      CREATE LOGIN security_login WITH PASSWORD = 'P@ssw0rd'; 
      GO
      GRANT VIEW SERVER STATE TO security_login;
      GO
      CREATE TRIGGER connection_deny_trigger
      ON ALL SERVER WITH EXECUTE AS 'security_login'
      FOR LOGON
      AS
      BEGIN
      <*Your conditional code goes here*>
          ROLLBACK;
      END;
      

      你可以定义你的工作来做到这一点:

      • 第 1 步:启用登录触发器
      • 第 2 步:检查开放用户 连接到您的报告数据库 循环直到值为0

        SELECT COUNT(*) from sysprocesses where spid in(
        SELECT session_id FROM sys.dm_exec_sessions WHERE is_user_process = 1) AND
        dbid= DB_ID('YourReportingDatabase')
        
      • 第 3 步:将 DB 设置为单用户和 恢复您的日志

      • 第 4 步:将 DB 重置为多用户并禁用登录触发器

      拉吉

      【讨论】:

        【解决方案4】:

        可以将报告重定向到不同的数据库名称吗?如果是,您可以创建 DB2 的数据库快照并从这些快照运行报告。每次日志还原后,您都会创建一个新快照并将其标记为“当前”快照,并且所有 报告都开始针对此快照运行。发送新日志时,会创建一个新快照,并且新报告与新快照相对,而旧的、正在运行的报告仍保留在先前的快照中。当最后一个报告使用旧快照完成并且不再有用户引用它时,可以删除它。这样就不会中断任何报告,但会以额外存储为代价:每个新日志都会导致旧快照开始执行受影响页面的“写时复制”。

        【讨论】:

          【解决方案5】:

          是否需要每小时进行一次备份,或者其他复制方法是否比您设置的自定义流程更能满足您的需求?您可以进行日志传送,可以将其配置为每小时传送一次,SQL Server 会在本地处理它,在恢复日志时阻止用户。

          我可以看到您的问题,并且我认为 SQL Server 不具备您正在寻找的功能(阻止新连接同时允许现有连接完成),但还有其他解决方法可以为您提供相同的功能复制功能,并可能更好地满足您的业务需求。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-06
            • 2019-10-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多