启动存储过程并即使断开连接也继续运行它

时间:2012-09-14 09:20:52

标签: sql-server stored-procedures asynchronous

我有一个数据库,可以在某种批次中处理数据,每批可能包含一百万条记录。我在控制台应用程序中处理数据,当我完成批处理时,我将其标记为完成(以避免在未被删除的情况下再次读取它),删除它并继续下一批。< / p>

我有以下简单的存储过程,它删除已处理的“批次”数据

CREATE PROCEDURE [dbo].[DeleteBatch]
(
    @BatchId bigint
)
AS

SET XACT_ABORT ON
BEGIN TRANSACTION
    DELETE FROM table1 WHERE BatchId = @BatchId
    DELETE FROM table2 WHERE BatchId = @BatchId
    DELETE FROM table3 WHERE BatchId = @BatchId
COMMIT
RETURN @@Error

我使用命令超时值为10分钟的NHibernate,并且DeleteBatch过程调用偶尔会超时。

实际上我不想等待DeleteBatch完成。我已将批处理标记为已完成,因此我想处理下一批或甚至退出我的控制台应用程序,如果没有更多待处理的批次。

我使用的是Microsoft SQL Express 2012。

是否有任何简单的解决方案告诉SQL服务器 - “启动DeleteBatch并异步运行它,即使我断开连接,我甚至不需要程序的结果”?

如果我可以为DeleteBatch设置较低的处理优先级,那也很好,因为其他查询比DeleteBatch更重要。

6 个答案:

答案 0 :(得分:3)

我对NHibernate不太了解。但是,如果您已经或可以在此方案中使用ADO.NET,那么您可以使用C#中的 SqlCommand.BeginExecuteNonQuery 方法轻松实现异步数据库操作。此方法启动异步执行不返回行的Transact-SQL语句或存储过程的过程,以便其他任务可以在语句执行时并发运行。

编辑:如果您真的想在数据库操作结束之前退出控制台应用程序,则必须在代码中手动创建线程并在这些线程中执行数据库操作。现在,当您关闭控制台应用程序时,这些线程仍然处于活动状态,因为默认情况下使用System.Thread.Thread创建的线程是前台线程。但是,已经说过要考虑创建多少个线程也很重要。在您的情况下,您必须为每个批次分配1个线程。如果批次数量非常大,则需要创建大量线程,这会占用大量CPU资源,甚至会长时间冻结您的操作系统。

我建议的另一个简单的解决方案是将BatchIds插入到某个数据库表中。在该表上创建INSERT TRIGGER。然后,此触发器将以BatchId作为参数调用存储过程,并执行所需的任务 希望它有所帮助。

答案 1 :(得分:1)

如果您的控制台应用程序是,而不是尝试删除批处理,只需将批处理ID写入“BatchIdsToDelete”表即可。然后,您可以使用每隔x分钟/秒运行一次的代理作业,删除给定批次ID的前x个百分比记录,并在解决下一个x%之前稍微休息一下。 也许值得一看?

答案 2 :(得分:1)

迟到聚会,但是如果其他人有此问题,请使用SQLCMD。使用express时,用户数量有限(我认为是2,但是自从我上次对express做很多事情以来,情况可能有所变化)。您可以拥有sqlcmd,运行查询,存储过程...

您可以使用Windows Scheduler启动sqlcmd。脚本,外表规则...

我使用它来管理大约3或4000个SQL Server Express实例,并通过Windows Scheduler安排它们的夜间维护。

您还可以创建和运行PowerShell脚本,它比sqlcmd更具通用性,并且使用范围更广。

答案 3 :(得分:0)

请看这篇文章解释如何执行reliable asynchronous procedure execution,包含的代码。 IS基于Service Broker。

尝试使用.NEt异步功能(如BeginExecute或任务等)的问题是调用不可靠:如果进程在过程完成之前退出,则在会话断开时在服务器中取消执行。

但你还需要查看任务本身,为什么是删除+10分钟?被争用阻止了吗?你在BatchId上缺少索引吗?使用Performance Troubleshooting Flowchart

答案 4 :(得分:0)

我需要同样的事情......

经过长时间的搜索,我找到了解决方案 最简单的方法

        SqlConnection connection = new SqlConnection();
            connection.ConnectionString = "your connection string";

            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connection.ConnectionString);
            builder.AsynchronousProcessing = true;

            SqlConnection newSqlConn = new SqlConnection(builder.ConnectionString);

            newSqlConn.Open();
    SqlCommand cmd = new SqlCommand(storeProcedureName, newSqlConn);

            cmd.CommandType = CommandType.StoredProcedure;
    cmd.BeginExecuteNonQuery(null, null);

答案 5 :(得分:0)

理想情况下,SQLConnection对象应该使用可选参数/属性,Web服务的URL,WCF或WebApi,或者尚未命名的内容,如果用户希望,则通知用户执行提前和/或完成状态通过使用众所周知的消息调用此URL。

理论上,DBConnection是可以自由实现的可扩展对象。但是,在这种方法可行之前,需要对实际可行和需要做的事情进行一些审查。