【问题标题】:Are temporary tables thread-safe?临时表是线程安全的吗?
【发布时间】:2010-10-02 18:57:18
【问题描述】:

我使用的是 SQL Server 2000,它的许多存储过程都广泛使用临时表。数据库流量很大,我担心创建和删除临时表的线程安全性。

假设我有一个创建一些临时表的存储过程,它甚至可以将临时表连接到其他临时表等。还可以说两个用户同时执行存储过程。

  • 是否有可能一个用户运行 sp 并创建一个名为 #temp 的临时表,而另一个用户运行同一个 sp 但由于数据库中已存在一个名为 #temp 的表而停止?

  • 如果同一个用户在同一个连接上执行同一个存储过程两次呢?

  • 是否还有其他可能导致两个用户查询相互干扰的奇怪场景?

【问题讨论】:

    标签: sql-server multithreading temp-tables


    【解决方案1】:

    对于第一种情况,不,这是不可能的,因为#temp 是一个本地临时表,因此对其他连接不可见(假设您的用户正在使用单独的数据库连接)。临时表名称是生成的随机名称的别名,您在引用本地临时表时引用该名称。

    在您的情况下,由于您在存储过程中创建本地临时表,that temp table will be dropped when the scope of the procedure is exited(请参阅“备注部分”)。

    在存储过程中创建的本地临时表会在存储过程完成时自动删除。该表可以被创建该表的存储过程执行的任何嵌套存储过程引用。调用创建该表的存储过程的进程无法引用该表。

    对于第二种情况,是的,你会得到这个错误,因为表已经存在,并且只要连接存在,表就会持续存在。如果是这种情况,那么我建议您在尝试创建表之前检查表是否存在。

    【讨论】:

    • 如何从一个连接并行执行相同的过程?
    • 您可以在同一个连接上执行相同的过程两次(不是并行),并且在第一次运行存储过程后不删除临时表。在这种情况下,临时表仍然存在。
    • 抱歉,@CasperOne,但您的信息不准确:在存储过程中创建的临时表会在 SP 完成时自动删除。在同一连接上调用此类 SP 两次没有问题。
    • @GerardoLima 通过参考文档更新了答案以反映您的评论。
    【解决方案2】:

    本地范围的临时表(带有单个#)在它们的末尾带有一个标识符,这使得它们是唯一的;多个调用者(即使使用相同的登录名)永远不应重叠。

    (试试看:从两个连接和相同的登录创建相同的临时表。然后查询 tempdb.dbo.sysobjects 以查看实际创建的表...)

    【讨论】:

      【解决方案3】:

      本地临时表是线程安全的,因为它们只存在于当前上下文中。请不要将上下文与当前连接混淆(来自MSDN:“在存储过程中创建的本地临时表在存储过程完成时会自动删除”),同一个连接可以安全地调用存储过程两次或多次创建一个本地临时表(如#TMP)。

      您可以通过从两个连接执行以下存储过程来测试此行为。该 SP 将等待 30 秒,因此我们可以确定两个线程将同时在它们自己的 #TMP 表版本上运行:

      CREATE PROCEDURE myProc(@n INT)
      AS BEGIN
          RAISERROR('running with (%d)', 0, 1, @n);
          CREATE TABLE #TMP(n INT);
          INSERT #TMP VALUES(@n);
          INSERT #TMP VALUES(@n * 10);
          INSERT #TMP VALUES(@n * 100);
          WAITFOR DELAY '00:00:30';
          SELECT * FROM #TMP;
      END;
      

      【讨论】:

      • 您提供的示例似乎无法证明您的观点:本地临时表only exist within the current context.Please don't confuse context with current connection。如果它像您描述的那样工作,那么应该在同一个会话中重复调用一个过程而不会出现问题,只要有问题的本地临时表是在过程的上下文中创建的,而不是在这个过程之外?
      • 谢谢,您链接的文章的临时表部分为我解释了一切:docs.microsoft.com/en-us/sql/t-sql/statements/…
      【解决方案4】:

      简短的回答是:

      临时表的隔离保证每个查询,并且有 在线程、锁或 并发访问。

      我不知道为什么这里的答案会谈论“连接”和线程的重要性,因为它们是编程概念,而 查询隔离是在数据库级别处理的

      本地临时对象在 SQL Server 中由 Session 分隔。如果您有两个查询同时运行,那么它们是两个完全独立的会话,不会相互干扰。登录名无关紧要,例如,如果您使用 ADO.NET 使用单个连接字符串(这意味着多个并发查询将使用相同的 SQL 服务器“登录名”),您的查询将全部仍然单独运行会议。连接池也无关紧要。本地临时对象(表存储过程)完全安全,不会被其他会话看到

      澄清这是如何工作的;虽然您的代码对本地临时对象有一个单一的通用名称,但 SQL Server 会在每个会话的每个对象上附加一个唯一的字符串,以使它们分开。您可以通过在 SSMS 中运行以下命令来看到这一点:

      CREATE TABLE #T (Col1 INT)
      
      SELECT * FROM tempdb.sys.tables WHERE [name] LIKE N'#T%';
      

      您将看到类似以下的名称:

      T_______________00000000001F

      然后,在不关闭该查询选项卡的情况下,打开一个新的查询选项卡并粘贴相同的查询并再次运行它。您现在应该会看到如下内容:

      T_______________00000000001F

      T_______________000000000020

      因此,每次您的代码引用#T 时,SQL Server 都会根据会话将其转换为正确的名称。分离都是自动处理的。

      【讨论】:

        【解决方案5】:

        临时表与会话相关联,因此如果不同的用户同时运行您的过程,则不会发生冲突...

        【讨论】:

          【解决方案6】:

          临时表仅在创建它们的查询或过程的上下文中创建。每个新查询都会在数据库上获得一个上下文,该上下文不包含其他查询的临时表。因此,名称冲突不是问题。

          【讨论】:

          • 我认为您可以对每个过程说,但不是每个查询。否则以后将无法查询同一个临时表。除非您的意思是查询 window ?
          【解决方案7】:

          如果您查看 temps 数据库,您可以在那里看到临时表,它们具有系统生成的名称。所以除了常规的死锁,你应该没问题。

          【讨论】:

            【解决方案8】:

            除非您使用两个井号##temp,否则临时表将是本地的,并且仅存在于与用户的本地连接

            【讨论】:

              【解决方案9】:

              首先让我们确保您使用的是真正的临时表,它们是以# 还是## 开头的?如果您在运行中创建实际表,然后反复删除和重新创建它们,您确实会遇到并发用户的问题。如果您正在创建全局临时表(以 ## 开头的临时表),您也可能会遇到问题。如果您不希望出现并发问题,请使用本地临时表(它们以 # 开头)。在 proc 结束时显式关闭它们也是一个很好的做法(或者当您正在谈论长的多步骤 proc 时,proc 不再需要它们)并在创建之前检查是否存在(如果存在则删除) .

              【讨论】:

                猜你喜欢
                • 2011-09-13
                • 2021-10-12
                • 2015-04-18
                • 2011-10-07
                • 2012-03-02
                • 2011-10-28
                • 2023-03-14
                • 2016-08-24
                • 2011-08-16
                相关资源
                最近更新 更多