【问题标题】:Execute a statement within a transaction without enlisting it in that transaction在事务中执行语句而不在该事务中登记它
【发布时间】:2019-10-22 00:00:53
【问题描述】:

我希望对批处理中的一些 SQL 语句进行性能分析。为此,我创建了一个记录执行时间的存储过程。

但是,我还希望能够回滚主批处理执行的更改,同时仍保留性能日志。

另一种方法是运行批处理,将性能数据复制到另一个数据库,从备份中恢复数据库,重新应用我想要分析的所有更改,再加上更多,然后重新开始。这比不包括登录事务的行为更耗时。

假设我们有这种情况:

BEGIN TRANSACTION
SET @StartTime = SYSDATETIME
-- Do stuff here
UPDATE ABC SET x = fn_LongRunningFunction(x)
EXECUTE usp_Log 'Do stuff', @StartTime
SET @StartTime = SYSDATETIME
-- Do more stuff here
EXEC usp_LongRunningSproc()
EXECUTE usp_Log 'Do more stuff', @StartTime
ROLLBACK

我如何才能将usp_Log 保存到表中的结果持久化,而不会将它们与事务中其他地方发生的更改一起回滚?

在我看来,理想情况下 usp_Log 不会以某种方式将自己加入可能回滚的事务中。

我正在寻找一种能够以最可靠的方式实施、编码或工作量最少、对所分析脚本的性能影响最小的解决方案。

编辑

正在分析的脚本非常耗时 - 从一个小时到几天不等 - 我需要能够在事务完成或回滚之前看到中间分析结果。我无法等待批处理结束才能查看日志。

【问题讨论】:

    标签: sql-server tsql transactions sql-server-2017


    【解决方案1】:

    您可以为此使用表变量。表变量和普通变量一样,不受ROLLBACK 的影响。在所有 COMMITROLLBACK 语句之后,您需要将性能日志数据插入到表变量中,然后在过程结束时将其插入到普通表中。

    【讨论】:

    • 对不起,我忘了说我需要能够在执行过程中看到日志条目。将日志条目存储在表变量中无济于事,因为批处理可能需要长达四天的时间,而且我需要更快地分析结果。
    • 您可以同时将数据写入表变量和普通表,然后,如果有回滚,则将变量中的记录复制到表中以替换记录已回滚。它不漂亮,但它会工作。
    • 如何更改transaction isolation level。这可能吗?
    • 我不这么认为,这与事务内部的语句与事务外部的语句在它们都影响相同数据时如何交互有关,但它不会影响事务时发生的情况完成。
    • 澄清一下:transaction isolation level 在这里没有任何影响,即不可能。
    【解决方案2】:

    这听起来有点矫枉过正(考虑到目的),但您可以创建一个 CLR 存储过程来接管进度记录,并在其中打开一个单独的连接以写入日志数据。

    通常,建议尽可能在 CLR 对象中使用context connection,因为它可以简化很多事情。但是,在您的特定情况下,您希望从上下文中解开(尤其是从当前事务中),因此定期连接是一种方法。

    请注意:如果您以前从未涉足 SQL Server 中的 CLR 编程,您可能会发现学习曲线有点过于陡峭。那,以及使其工作所需的服务器重新配置量(SQL Server 实例和底层操作系统)似乎也非常昂贵,不值得麻烦。不过,我认为这是一种可行的方法。

    【讨论】:

    • 通常情况下,是的...但不幸的是,此环境中不允许使用 SQLCLR。
    【解决方案3】:

    因此,正如 Roger 上面提到的,SQLCLR 是一种选择。但是,由于不允许使用 SQLCLR,所以你运气不好。

    在 SQL Server 2017 中还有另一种选择,那就是使用 SQL Server 可扩展性框架和对 Python 的支持。

    您可以使用它来让 Python 代码回调到您的 SQL Server 实例并执行usp_log 过程。

    【讨论】:

      【解决方案4】:

      另一个相当模糊的选项是将其他会话绑定到长时间运行的事务以进行监视。

      在事务开始时调用sp_getbindtoken并显示绑定令牌。

      然后在另一个会话中调用sp_bindsession,就可以检查事务的中间状态了。

      或者您可以使用 (NOLOCK) 读取日志。

      或者您可以使用RAISERROR WITH LOG 向客户端发送调试消息并将它们镜像到SQL Log。

      或者您可以使用custom user-configurable trace events,并在 SQL Trace 或 XEvents 中监控它们。

      或者您可以使用配置为不传播事务的Loopback linked server

      【讨论】:

        猜你喜欢
        • 2020-12-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-21
        相关资源
        最近更新 更多