每当将一行插入某个表时,我想添加一个触发器。触发器将触发 CLR 存储过程,该过程将执行数据库工作以外的事情,例如将 JSON 字符串写入 txt 文件并更新不同类型数据库的系统日志。
我的手被绑在使用这项技术上。最初我们打算编写一个 Windows 服务,它会定期扫描表,然后在插入新记录时运行该程序。
我的问题是:
我能否将 DLL 托管在不同的服务器上以便它在那里运行,或者当我部署 CLR 存储过程 dll 时,它是否始终在 SQL Server 数据库实例中运行?
我问的原因是我们不希望此代码减慢数据库服务器的速度,这可能会通过在代码中执行这些其他任务来实现。
这将如何工作?如果你想在远程服务器上运行代码,你仍然需要本地服务器上的代码才能进行远程调用。
虽然,您可以设置一个 Express Edition 实例来托管 SQLCLR 代码(它需要是一个存储过程和/或函数而不是一个触发器),然后为该 Express 实例创建一个链接服务器,然后让本地表上的 T-SQL 触发器通过链接服务器执行 SQLCLR 存储过程和/或函数。以这种方式,调用远程代码的本地代码是纯 T-SQL。您甚至不需要在本地实例上启用 CLR 集成。
无论您在触发器内部做什么,无论是 T-SQL 还是 SQLCLR 触发器,都将阻止该 DML 语句的完成。使用 SQLCLR 并没有真正改变那里的任何东西。
触发器在以下方面的确切要求是什么:
这些步骤/操作是否需要受事务约束,以便如果 INSERT 操作失败,txt 文件不存在并且日志条目不存在?
找到一种异步执行此操作的方法将是理想的(正如Jonathan所提到的)。但是,进行 Web 服务调用会删除事务方面,因此您可能会获得实际上不存在的行的文件和日志条目。
使用 NT 服务对 INSERT 操作的影响最小,除非您正在使用
WITH (NOLOCK)
(您不应该这样做),否则它只会看到成功的 INSERT 操作的结果。但是,该服务已断开连接,并且比触发器模型有更多的延迟,您将需要找到一种方法来跟踪哪些条目是“新的”(NT 服务将需要跟踪一个DATETIME
或可能会使用一个ROWVERSION
值并与@@DBTS
每次比较)。如果您的表已经设置了DATETIME
或DATETIME2
列INSERT
,那么您可以利用该现有列,否则您将需要添加一个DATETIME
/DATETIME2
/ROWVERSION
柱子。或者,如果您有基于 IDENTITY 或 Sequence 的 PK,那么您可以使用该值而不是添加列,NT 服务只需要跟踪最近使用的ID
. 你会把这个值存储在哪里?文本文件?注册表?云端”?(开玩笑说最后一个 ;-)另一方面,您可能会使用 Service Broker 将这两个部分匹配在一起。当触发器触发时,它可以向 Service Broker 队列添加一条消息,包括
INSERTED
表中的任何必要信息。这允许它是异步的。然后,在另一端,队列读取器可以使用 SQLCLR 存储过程作为“激活过程”来获取该信息并采取适当的操作(创建 JSON 文件、在其他系统上记录条目等)。此模型可能不需要 Service Broker。您可以设置自己的队列表,让 T-SQL 触发器将条目转储到其中,然后创建一个从队列表中读取并调用 SQLCLR 存储过程和/或函数的 SQL Server 代理作业异步(无论它们位于本地服务器上还是跨链接服务器),然后在成功处理记录后删除记录。
简短的回答,没有。
您可能的替代方法是让触发器/clr 触发托管在另一台服务器上的异步 Web 服务调用。这将允许您的进程在另一台服务器上执行并且对 SQL Server 的影响最小。
据我所知,没有在与 SQL Server 本身不同的机器上运行 CLR 代码(在数据库中)的实用方法。
鉴于您的要求,我会推荐一个类似于 Windows 服务解决方案的替代方案。从性能的角度来看,定期轮询表可能不是很好。相反,您能否在触发器中使用 CLR 代码将一些输出数据附加到
.txt
文件,并让另一台机器上的 Windows 服务间歇性地轮询该文本文件?这样,Windows 服务就可以驻留在另一台服务器上,异步运行并使用网络共享。作为奖励,Windows 服务不会在具有触发器的表上放置锁。