SQL Server从触发器触发异步更新?

时间:2011-09-01 14:37:57

标签: sql-server asynchronous sql-server-2000

如果用户在表中插入行,我希望SQL Server执行一些额外的处理 - 但不是在用户事务的上下文中。

e.g。用户提供对文件夹的读访问权:

UPDATE Folders SET ReadAccess = 1
WHERE FolderID = 7

就用户而言,我希望这是原子操作的结束。实际上,我现在必须找到所有子文件和文件夹并给它们ReadAccess

EXECUTE SynchronizePermissions

这是一个可能很长的操作(超过2秒)。我希望这个冗长的操作发生在“稍后”。它可能在0秒后发生,在碳单元有机会考虑之前,异步更新就完成了。

如何在需要时(即触发)运行异步所需的操作

理想将是:

CREATE TRIGGER dbo.Folders FOR INSERT, UPDATE, DELETE AS
   EXECUTEASYNCHRONOUS SynchronizePermissions

CREATE TRIGGER dbo.Folders FOR INSERT, UPDATE, DELETE AS
   EXECUTE SynchronizePermissions WITH(ASYNCHRONOUS)

现在这是一个触发器:

CREATE TRIGGER dbo.Folders FOR INSERT, UPDATE, DELETE AS
   EXECUTE SynchronizePermissions

并且用户每次对Folders表进行更改时都被迫等待3秒。

我考虑过在用户上创建预定任务,每分钟运行一次,并检查PermissionsNeedSynchronizing标志:

CREATE TRIGGER dbo.Folders FOR INSERT, UPDATE, DELETE AS
   UPDATE SystemState SET PermissionsNeedsSynchronizing = 1

计划任务二进制文件可以检查此标志,如果标志打开则运行:

DECLARE @FlagValue int
SET @FlagValue = 0;

UPDATE SystemState SET @FlagValue = PermissionsNeedsSynchronizing+1
WHERE PermissionsNeedsSynchronizing = 1

IF @FlagValue = 2
BEGIN
   EXECUTE SynchronizePermissions

   UPDATE SystemState SET PermissionsNeedsSynchronizing = 0
   WHERE PermissionsNeedsSynchronizing = 2
END

计划任务的问题是:   - 它可以运行的最快速度是每60秒   - 它是一个民意调查解决方案   - 它需要一个可执行文件

我更喜欢的是SQL Server可以触发计划任务的方式:

CREATE TRIGGER dbo.Folders FOR INSERT, UPDATE, DELETE AS
   EXECUTE SynchronizePermissionsAsychronous


CREATE PROCEDURE dbo.SynchronizePermissionsAsychronous AS

   EXECUTE sp_ms_StartWindowsScheduledTask @taskName="SynchronousPermissions"

这个问题是: - 没有sp_ms_StartWinodowsScheduledTask系统存储过程

所以我正在寻找更好解决方案的想法。


更新:上一个例子是一个问题,现在已经有五年没有好的解决方案了。 3年前的一个问题是没有好的解决方案,我需要在插入/更新后更新元数据列。元数据在在线交易处理中计算的时间太长,但我可以在3或5秒后出现:

CREATE TRIGGER dbo.UpdateFundsTransferValues FOR INSERT, UPDATE AS

UPDATE FundsTransfers
SET TotalOrderValue = (SELECT ....[snip]....),
    TotalDropValue = (SELECT ....,[snip]....)
WHERE FundsTransfers.FundsTransferID IN (
    SELECT i.FundsTransferID
    FROM INSERTED i
)

我今天遇到的问题是在过渡插入或修改行后异步更新某些元数据的方法:

CREATE TRIGGER dbo.UpdateCDRValue FOR INSERT, UPDATE AS

UPDATE LCDs
SET CDRValue = (SELECT ....[snip]....)
WHERE LCDs.LCDGUID IN (
    SELECT i.LCDGUID
    FROM INSERTED i
)

更新2 :我考虑过创建本机或托管的dll并将其用作扩展存储过程。问题是:

  • 你不能编写二进制文件
  • 我现在可以这样做了

3 个答案:

答案 0 :(得分:6)

使用队列表,并使用不同的后台进程从队列中提取并处理它们。根据定义,触发器本身就是用户交易的一部分 - 这正是他们经常被劝阻的原因(至少人们被警告不要在触发器中使用昂贵的技术)。

答案 1 :(得分:1)

创建SQL代理作业并使用sp_start_job运行它。不应该等待完成

但是,您需要适当的权限才能运行作业

  

SQLAgentUserRole和SQLAgentReaderRole的成员只能启动作业   他们拥有。 SQLAgentOperatorRole的成员可以启动所有本地   工作包括其他用户拥有的工作。的成员   sysadmin可以启动所有本地和多服务器作业。

这种方法的问题在于,如果作业已经运行,则在完成之前无法启动

否则请使用Aaron建议的队列表,它更干净,更好

答案 2 :(得分:0)

我们前段时间遇到过这个问题,我找到了一个效果很好的解决方案。我确实有一个在后台运行的进程 - 但就像你一样,我不希望它每60秒轮询一次。

以下是步骤:

(1)我们的触发器不会自动运行db update。它只是将“标志文件”抛出到由后台进程监视的文件夹中。

(2)后台进程使用Windows Change Notification监视该文件夹(这是非常酷的部分,因为您不必轮询文件夹 - 您的进程会一直睡到Windows通知它已出现文件)。每当Windows唤醒后台进程时,它都会运行db update。然后它删除标志文件,再次进入休眠状态并告诉Windows在文件夹中出现另一个文件时将其唤醒。

这与您所描述的完全一样:触发更新在主数据库事件后不久运行,而 voila ,用户无需等待额外的几秒钟。我只是喜欢它。

您不一定需要编译自己的可执行文件来执行此操作:许多脚本语言可以使用Windows更改通知。我在Perl中编写了后台进程,只需几分钟就能完成它。