SQL Server如何处理事务INSERT中的SELECT语句?

时间:2011-06-27 16:10:53

标签: sql-server performance transactions

作为零售结算流程的一部分,有一个事务存储过程,它从18个表中的每个表中进行选择,并将它们插入到单独的数据库中,以便以后进行大型机处理。这个过程显示了一些奇怪的计时行为,我认为这是因为对SQL Server中事务的工作方式存在根本性的误解。

我认识到这不是这个问题的最佳架构,而且正在开发新的解决方案,但与此同时,我需要改进这个过程。

存储过程基于用户请求运行,如下所示:

BEGIN TRANSACTION

INSERT INTO Table1
        (Column1,
        Column2,
        Column3,
        Column4,
        Column5,
        Column6,
        Column7,
        Column8)
    SELECT
        Column1,
        Column2,
        Column3,
        Column4,
        Column5,
        Column6,
        Column7,
        Column8
    FROM
        OLTPTable T
    INNER JOIN
        LookupTable1 L
    ON  
        T.Foreign = L.Key

INSERT INTO Table2
        (Column1,
        Column2,
        Column3)
    SELECT
        Column1,
        Column2,
        Column3
    FROM
        OLTPTable2 T
    INNER JOIN
        LookupTable2 L
    ON  
        T.Foreign = L.Key

INSERT INTO Table3
        (Column1,
        Column2,
        Column3,
        Column4,
        Column5,
        Column6)
    SELECT
        Column1,
        Column2,
        Column3,
        Column4,
        Column5,
        Column6
    FROM
        OLTPTable3 T
    INNER JOIN
        LookupTable3 L
    ON  
        T.Foreign = L.Key

-- Through Table 18 and OLTP Table 18

COMMIT TRANSACTION

日志记录看起来像这样:

Table1      0.2 seconds 354 rows
Table2      7.4 seconds 35 rows
Table3      3.9 seconds 99 rows

行数或连接的复杂性与时间之间没有明确的相关性。

我的问题是 - 在这样一个漫长的程序中,交易的影响是什么?是否在开始时锁定子选择中的所有表?一次一个?它是否在等待源表可用于锁定,这导致等待?

3 个答案:

答案 0 :(得分:4)

使用默认的READ COMMITTED隔离级别,Read(共享)锁定将在每个SELECT的持续时间内存在。不适用于交易。

要更改此设置,您需要更高的REPEATABLE_READ才能保持共享(读取)锁定,直到事务结束。

注意:

  • 锁定粒度(行,页面等)与此持续时间分开
  • 其他进程将能够读取SELECTed表

您的INSERT持续时间将受到大量条件的影响。一些,:

编辑:

经过深思熟虑后,使用sp_getapplock等可以更好地维护应用级并发性。

答案 1 :(得分:2)

在我看来,在这种情况下,使用或不使用交易不会对性能造成太大影响。我建议您需要单独调整每个插入(考虑到插入的性能不仅取决于子选择,还取决于插入 - 目标表中的索引越多,插入性能越差;您可能也选择了错误某些表的聚簇索引)。

答案 2 :(得分:2)

在什么样的隔离级别?

对于写入部分(插入),无论隔离级别如何,一切都是相同的:所有插入的行都以X模式锁定,直到事务结束。行锁可以escalate到表锁。

如果隔离级别保留在默认的 read committed 并且数据库上的read-committed-snapshot-isolation选项为OFF,则会发生如下情况:每个select将锁定一行在S模式下的时间并将立即释放。

可重复读取隔离下,SELECTs获取的S锁将保留到事务结束,并且它们可能会升级到表S锁。

可序列化读取隔离下,SELECT将获取范围锁而不是行锁,并将保留它们直到事务结束。同样,可能会发生锁定升级。

快照隔离下,SELECTs根本没有获取任何锁(忽略了一些关于模式稳定性锁的技术细节),它们将从版本tore中读取任何锁定的行。读取的版本对应于事务开始时的值的快照。

在数据库上启用读取 - 提交 - 快照 - 隔离时,读取已提交隔离,SELECT将不会获取任何锁定,如果行被锁定,它们将从版本存储读取。读取的版本对应于语句开头的值的快照。

现在回到你的问题,为什么你会看到性能上的差异?与任何性能问题一样,最好采用调查方法,Waits and Queues是一个很好的方法。一旦你研究了根本原因,就可以提出一个合适的解决方案。