我能在 SQL Server 中开启一个不可中止的事务吗?

4
我是一个有用的助手,可以为您翻译文本。
我正在寻找与SQL事务类似的内容。 我需要事务提供的通常保护措施,但我不希望它减慢其他人的速度。
想象客户端A连接到数据库并运行以下命令:
BEGIN TRAN
SELECT (something)
(Wait a few seconds maybe.)
UPDATE (something)
COMMIT

在SELECT和UPDATE之间,客户端B过来执行一次查询操作,在正常情况下,它必须等待A执行COMMIT。我希望的是,当A打开其事务时,如果B也来执行查询操作,那么A的事务应该立即回滚并使其后续命令失败。B只会经历微小的延迟。(请注意,SELECT和UPDATE只是演示性的命令。)
更新...
我有一个优先级高的任务(客户B),有时(每个月左右)会出现SQL超时错误,还有一个低优先级的任务(客户A)有一个事务导致了这个超时。我宁愿让低优先级的任务失败,并在下一个循环中重新尝试。最终,我通过完全消除事务并用一组非正式的标志替换它们来解决了这个问题。这些查询被重构为只在正确的标志集被引发时才执行某些操作,并添加了一些清理遗留记录的内容,这些记录在以前的回滚中已清除。我通过消除事务来解决我的事务问题。

5
如果您的交易如此不重要,以至于您愿意接受回滚并导致后续命令失败,那么为什么还要使用交易呢? - Joe Stefanelli
一项事务仍将防止半应用的事务。当然,如果您试图最小化ROLLBACK可能需要的时间,那么它可能会比完成事务本身还要长。 - Tom H
“我所期望的是客户A向客户B开放其交易。”这种期望在商业和技术角度上都是没有意义的。它违背了交易的基本含义。 - blispr
@Joe - 我可能更关心不耽误高优先级任务的进度,而不是我的低优先级任务何时完成。 - billpg
@blspr - 相反,这是非常合理的。 :) B 不会以任何方式进入由 A 打开的事务。就像如果一个事务被网络故障中断一样,数据库会忠实地回滚所有未提交的内容并释放等待查询的块。 - billpg
4个回答

6

使用 SNAPSHOT 隔离级别可以避免 B 的阻塞。在 A 发出 BEGIN TRANSACTION 命令之前,B 将看到数据处于它们之前的状态。除非 B 修改数据,否则它们永远不会相互阻塞。


这是一个很好的解决方案。使用这个隔离级别,“写入者不会阻塞读取者”。 - blispr

2

这是前后颠倒的。

你不能让较晚的客户端中止较早的事务:那样会造成混乱。

你可以使用快照隔离,这样客户端B就有了一致的视图,并且不会被客户端A(大多数情况下)阻塞。另外,Wikipedia提供更一般的信息。

也许可以更详细地描述你的问题,这样我们可以为你提供建议...


@billpg:如果它每月运行一次,那么它并不是高优先级。为什么你的TXNs需要这么长时间才会导致超时呢?你已经通过削减事务来隐藏设计、查询或索引中的缺陷。或者以上所有情况都有可能。 - gbn
不,它每分钟运行一次。最坏情况偶尔会随机发生,导致每月出现超时故障。由于在同一数据库上的多台机器上进行了大量处理,因此交易需要很长时间。 - billpg
@billpg:增加超时时间。虽然我误读了,但你仍然为每月一次的事件而入侵你的事务和数据完整性。无论如何,这是你的数据库,而不是我的... - gbn

2
虽然 乐观并发 并不是一种交易,但它可能会很有用——在LINQ2SQL等中默认使用它。
其基本思想是读取数据,可以独立地进行修改,然后将数据写回并带上“检查”(这大致相当于 Compare and Swap)。如果检查失败,应用程序就要决定该怎么做(重新启动进程、继续执行或者失败)。
显然,这种方法并不适用于所有情况,并且可能无法检测到许多交互作用,比如在“读取”和“写入”之间添加的新项目。实际的读取和写入都可以在具有适当隔离级别的单独事务中进行;这些单独的事务可能允许插入其他的事务。
当然,根据具体的问题和交互作用,不同的隔离级别和/或更细粒度的锁定可能已经足够了。
祝编码愉快。

我的一系列标志(请参见问题中的更新)实现了与您所描述的非常相似的效果。 - billpg

1

我见过使用的一种方法(但恐怕我手头没有任何代码),就是让事务A生成另一个进程,然后监视该事务。如果它看到了由事务引起的任何阻塞,那么它将立即向spid发出KILL指令。

如果我能找到这个代码,我会在这里添加它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接