【问题标题】:How to notify a windows service(c#) of a DB Table Change(sql 2005)?如何通知 Windows 服务(c#)数据库表更改(sql 2005)?
【发布时间】:2010-12-06 06:51:11
【问题描述】:

我在 SQL2005 数据库中有一个负载很重的表(许多插入/更新/删除)。我想尽可能接近实时地对所有这些更改进行一些后期处理(异步以免以任何方式锁定表)。我查看了许多可能的解决方案,但似乎无法找到一个感觉正确的巧妙解决方案。

这种后期处理也相当繁重,以至于 Windows 侦听器服务实际上会将处理传递给许多机器。然而,这部分应用程序已经启动并运行,完全异步,而不是我需要帮助的地方——我只想提一下,因为它影响了设计决策,因为我们不能只在DB完成处理。

所以,简单的问题仍然存在:表中的数据发生变化,我想在远程服务器上用 C# 代码进行一些处理。

目前我们已经想出了使用 sql 触发器,它执行“xp_cmdshell”来启动一个 exe,该 exe 引发 Windows 服务正在侦听的事件。这感觉很糟糕。

但是,我在网上看到的其他解决方案也感觉相当复杂。例如,设置 SQLCacheDependancy 还需要设置服务代理。另一种可能的解决方案是使用 CLR 触发器,它可以调用 Web 服务,但是网上有很多警告说这是一种不好的方法,尤其是在性能至关重要的时候。

理想情况下,我们不会依赖表更改,而是会拦截应用程序内部的调用并从那里通知服务,但不幸的是,尽管我们有一些旧应用程序也对数据进行更改,并且监控表是唯一的目前是集中的地方。

任何帮助将不胜感激。

总结:

  • 需要实时响应表数据变化
  • 性能至关重要
  • 预计会有大量流量
  • 轮询和计划任务不是一个选项(或实时)
  • 实现的服务代理太大(但可能是唯一的解决方案?)
  • 尚未排除 CLR 代码,但如果建议,则需要执行
  • 监听器/监听器可能是远程机器(可能是同一个物理网络)

【问题讨论】:

  • 在远程位置事件或轮询不会有很大的不同,因为网络时间延迟本身高于几毫秒的轮询。如果您查看任何架构,事件本身也在某个时候基于轮询。精心设计的投票也不错。

标签: c# sql-server-2005 triggers sqlclr


【解决方案1】:

由于您说该表上运行了许多插入,因此批处理可能更适合。

为什么只创建一个计划作业,它处理由标志列标识的新数据,并以大块的形式处理数据?

【讨论】:

  • 感谢您的回复。不幸的是,我理解这个解决方案不是实时的,也不是任何需要轮询机制的解决方案。
【解决方案2】:

这可以通过多种方式完成。下面的方法很简单,因为您不想使用 CLR 触发器和 sqlcmd 选项。

  • 您可以创建普通的插入触发器,而不是使用 CLR 触发器,它会在每次插入时更新专用跟踪表。

  • 并开发专门的窗口服务,它会主动轮询跟踪表,如果数据有任何变化,则更新远程服务,并将跟踪表中的状态设置为完成(因此不会再次被选中)。

编辑:

我认为 ADO.Net 的 Microsoft 同步服务可以为您工作。查看以下链接。可能对你有帮助

【讨论】:

  • 感谢您的回复。我们真的很喜欢它是一个通知而不是使用轮询。不幸的是,轮询已被讨论并排除为一种解决方案,而且也不是实时的。再次感谢
  • 再次感谢。我看过这个,但像服务代理一样是尝试找到解决方案的一种相当迟钝的方式。在大多数情况下,它是一种监视所有更改(包括架构更改)的方法。文章还提到它有许多缺点,考虑到我的“实时”和“高性能”要求,这将使其难以实现。
【解决方案3】:

使用典型的触发器在数据库上触发 CLR。此 CLR 只会使用 Win32_Process 类远程启动程序:

http://motevich.blogspot.com/2007/11/execute-program-on-remote-computer.html

【讨论】:

  • 感谢您的回复。这与我们使用 EXEC sp_cmdShell 启动进程的方法并不太相似。您的方法确实解决了远程启动它的能力,但是我仍然认为该方法不是最好的方法……即数据更改、触发触发器、运行 CLR、启动 exe(本地或远程)、触发事件、Windows服务监听事件。这似乎有点疯狂,但我开始觉得这是唯一的方法。再次感谢
  • 至少使用这种方法,它实际上是在远程机器上实时开始处理,它不会发出一个必须等​​待一些轮询来处理它的请求。您可以删除 clr 并使用触发器调用 EXEC sp_cmdShell XYZ.exe,并且 XYZ.exe 使用 Win32_Process 类远程运行 exec。恐怕你无能为力了。
【解决方案4】:

您确实没有那么多方法可以检测 SQL 2005 中的更改。您已经列出了其中的大部分。

查询通知。这是支持 SqlDependency 及其衍生产品的技术,您可以在The Mysterious Notification 上阅读更多详细信息。但 QN 旨在使结果无效,而不是主动通知更改内容。你只会知道表发生了变化,而不知道发生了什么变化。在繁忙的系统上,这是行不通的,因为通知会持续不断地出现。

日志阅读。这是事务复制使用的方式,并且是检测更改的最少干扰方式。不幸的是,仅适用于内部组件。即使您设法理解日志格式,问题是您需要引擎的支持才能将日志标记为“正在使用”,直到您阅读它,否则它可能会被覆盖。只有事务复制可以做这种特殊的标记。

数据比较。依靠时间戳列来检测更改。也是基于拉取的,非常具有侵入性,并且在检测删除方面存在问题。

应用层。这是理论上的最佳选择,除非应用程序范围之外的数据发生更改,在这种情况下它会崩溃。在实践中,总是在应用程序范围之外发生变化。

触发器。最终,这是唯一可行的选择。所有基于触发器的更改机制都以相同的方式工作,它们将更改通知排队到监视队列的组件。

总是有人建议进行紧密耦合的同步通知(通过 xp_cmdshell、xp_olecreate、CLR、使用 WCF 通知,等等),但所有这些方案在实践中都失败了,因为它们存在根本缺陷:
- 它们不考虑事务一致性和回滚
- 它们引入了可用性依赖项(除非通知的组件在线,否则 OLTP 系统无法继续运行)
- 它们执行得非常糟糕,因为每个 DML 操作都必须等待某种形式的 RPC 调用完成

如果触发器实际上并没有主动通知侦听器,而只是将通知排队,那么监控通知队列就会出现问题(当我说“队列”时,我指的是任何充当队列的表)。监控意味着在队列中拉取新条目,这意味着正确平衡检查频率与更改负载,并对负载峰值做出反应。这一点都不是小事,实际上是非常困难的。但是,SQL Server 中有一条语句具有阻塞的语义,无需拉取,直到更改可用:WAITFOR(RECEIVE)。这意味着服务代理。您在帖子中多次提到 SSB,但您理所当然地害怕部署它,因为存在很大的未知数。但现实情况是,到目前为止,它最适合您描述的任务。

您不必部署完整的 SSB 架构,其中通知一直传递到远程服务(无论如何这将需要远程 SQL 实例,甚至是 Express 实例)。您需要做的就是将检测到更改的时刻(DML 触发器)与传递通知的时刻(提交更改之后)分离。为此,您只需要一个本地 SSB 队列和服务。在触发器中,您SEND 向本地服务发出更改通知。在原始 DML 事务提交后,服务过程activates 并传递通知,例如使用 CLR。你可以在Asynchronous T-SQL看到一个类似的例子。

如果您走这条路,则需要学习一些技巧以实现高吞吐量,并且您必须了解 SSB 中消息有序传递的概念。我建议您阅读以下链接:

关于检测更改的方法,SQL 2008 显然添加了新选项:Change Data Capture and Change Tracking。我强调“显然”,因为它们并不是真正的新技术。 CDC 使用日志阅读器并基于现有的事务复制机制。 CT 使用触发器,与现有的 Merge 复制机制非常相似。它们都适用于需要同步的偶尔连接系统,因此不适合实时更改通知。他们可以填充更改表,但您需要监控这些表的更改,而这正是您从哪里开始的。

【讨论】:

  • 非常感谢您的全面回复。我可以清楚地告诉你理解问题域。使用 SSB 的可能性肯定存在,我需要额外阅读以确保。再次感谢
  • 关于查询通知和 SqlDependency,在多个表上创建 DLM 触发器是否有意义,这将在每次更改时在队列中创建一个项目并使 SqlDependency 查询依赖于该特定队列?跨度>
【解决方案5】:

在类似的情况下,我们正在使用将消息写入队列 (MSMQ) 的 CLR 触发器。用 C# 编写的服务正在监视队列并进行后处理。 在我们的例子中,这一切都是在同一台服务器上完成的,但您可以将这些消息直接发送到另一台机器上的远程队列,完全绕过“本地侦听器”。

触发器调用的代码如下:

public static void SendMsmqMessage(string queueName, string data)
{
    //Define the queue path based on the input parameter.
    string QueuePath = String.Format(".\\private$\\{0}", queueName);

    try
    {
        if (!MessageQueue.Exists(QueuePath))
            MessageQueue.Create(QueuePath);

        //Open the queue with the Send access mode
        MessageQueue MSMQueue = new MessageQueue(QueuePath, QueueAccessMode.Send);

        //Define the queue message formatting and create message
        BinaryMessageFormatter MessageFormatter = new BinaryMessageFormatter();
        Message MSMQMessage = new Message(data, MessageFormatter);

        MSMQueue.Send(MSMQMessage);
    }
    catch (Exception x)
    {
        // async logging: gotta return from the trigger ASAP
        System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(LogException), x);
    }
}

【讨论】:

  • 感谢您的回复。我认为这对我来说与使用 Service Broker 有相同的缺点。我们要与之交谈的侦听器服务最初是为了从 MSMQ 中脱离出来而编写的。因此,必须实现 MSMQ 似乎是一种倒退,并且再次将我们暴露于必须轮询队列以获取新消息的原始问题。我们还不如轮询数据库的变化?但是,如果这是唯一的出路,它是一种更简洁的解决方案,我肯定更喜欢使用 xp_cmdshell。
猜你喜欢
  • 2014-11-16
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 2017-04-10
  • 1970-01-01
  • 1970-01-01
  • 2011-03-16
  • 1970-01-01
相关资源
最近更新 更多