【问题标题】:How is a T-SQL transaction not thread-safe?T-SQL 事务如何不是线程安全的?
【发布时间】:2009-02-12 19:56:02
【问题描述】:

以下(经过清理的)代码有时会产生这些错误:

无法删除表“database.dbo.Table”,因为它不存在或您没有权限。
数据库中已经有一个名为“表”的对象。

begin transaction  
    if exists (select 1 from database.Sys.Tables where name ='Table') 
        begin drop table database.dbo.Table end 

    Select top 3000 *
    into database.dbo.Table
    from OtherTable
commit

select * from database.dbo.Table

代码可以同时运行多次。有人知道它为什么会坏吗?

【问题讨论】:

    标签: sql-server multithreading tsql transactions transaction-isolation


    【解决方案1】:

    我能问一下你为什么要先这样做吗?您真的应该考虑使用临时表或提出其他解决方案。

    我不肯定 DDL 语句在事务中的行为与 DML 语句相同,并且我看到一篇博客文章具有奇怪的行为并在 DDL 中创建存储过程。

    除此之外,您可能还想验证您的事务隔离级别并将其设置为序列化。

    编辑

    基于快速测试,我在两个不同的连接中运行了相同的 sql,当我创建表但没有提交事务时,第二个事务被阻塞。所以看起来这应该有效。我仍然会警告这种类型的设计。

    【讨论】:

    • 非常感谢。我们可能会改变设计,但我仍然对这不起作用感到困惑。
    • 改变设计;使用临时表 (#Table) 或 RAM 表变量 (@Table) ...你最终会更快乐!
    • +1 serializable 确实可以解决这个问题,但是这个设计是有缺陷的
    • 我同意这个设计有缺陷;-)
    【解决方案2】:

    您在代码的哪一部分阻止了对该资源的多次访问?

    begin transaction  
        if exists (select 1 from database.Sys.Tables where name ='Table') 
            begin drop table database.dbo.Table end 
    
        Select top 3000 *
        into database.dbo.Table
        from OtherTable
    commit
    

    Begin transaction 没有执行。它只是为添加到表的任何行设置提交/回滚场景。

    (如果存在,则丢弃)是一个竞争条件,以及使用 (select..into) 重新创建的表。多人同时进入该代码肯定会导致各种错误。一些创建其他人刚刚销毁的表,另一些删除不再存在的表,还有一些删除一些正忙于插入的表。啊!

    考虑其他人的临时表建议,或者在关键资源繁忙时使用应用程序锁来阻止其他人输入此代码。删除/创建事务不是您想要的。

    【讨论】:

    • +1 用于启动竞争条件并表示删除/创建事务不是您想要的,-1 用于建议应用程序锁定
    • 应用锁没有问题,sql server也为它们提供了便利。
    【解决方案3】:

    如果您只是在此过程中使用此表,我建议您使用临时表或 ram 表,具体取决于数据量。我经常使用 ram 表来避免任何交易成本并节省磁盘活动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      相关资源
      最近更新 更多