在数据库表中使用SPID(而不是表变量)


8

用于预订事物的交易数据库...

我们的供应商被要求用@tablevariables替换#temptables(由于繁重的编译锁),但他们替换为一个实际表,该表将SPID作为列添加,以确保存储过程仅作用于适用的行。

您认为这种操作方法有任何风险吗?在所有事务都隔离在它们自己的事务中之前,我担心我们最终可能会将该表锁定一堆,但他们的意见是SQL使用行级锁定并且不会创建更多的锁。

SQL Server版本:2016 Enterprise-13.0.5216.0


CREATE TABLE dbo.qryTransactions (
    ID int IDENTITY (0,1) NOT NULL CONSTRAINT pk_qryTransactions PRIMARY KEY CLUSTERED,
    spid int NOT NULL,
    OrderID int,
    ItemID int,
    TimeTransactionStart datetime,
    TimeTransactionEnd datetime,
...other fields
    )

CREATE INDEX idx_qryTransactions_spidID ON qryTransactions (spid, ID) INCLUDE (ItemID, OrderID, TimeTransactionStart, TimeTransactionEnd)


1
这取决于。并发使用表的会话数,会话的活跃度,表中有多少条记录,此新表中的记录是否被清除或保留等?5节,没什么大不了的。500个会话,很有可能您会遇到阻塞,而每个会话本地的临时表/变量将无法看到它们。
John Eisbrener

您正在使用哪个版本的sql server?
Anthony Genovese

2016年企业-13.0.5216.0
退出

每会话命中表的记录将是1-50 ...将被清除,因此表本身可能一次不会获得超过1000行...并发会话可能大约50 ....
outjet

1
如果您被迫沿着这条路走(请尝试避免使用此路径),我会认真考虑对spid值进行分区,确保将表上的锁升级设置为AUTO。然后,至少可以在切换和截断操作中擦除特定spid的数据。
乔纳森·费特

Answers:


5

杂乱无章的内容超出了注释块的范围...并且想突出显示OP为回应Ray的回答而做出的注释:

  • 父proc(Common_InsertOrders_1)创建一个临时表
  • 子proc(InsertOrders)查询临时表
  • 正在为子proc(InsertOrders)查看编译锁

稍微切线一分钟...在这种情况下将发生Sybase ASE ...

  • 每个临时表都有一个唯一的对象ID(当然,该对象ID可以在某个时候重用,但这很少见,并且对于并发会话肯定不会发生)
  • Sybase ASE通常会在子proc的每次执行中强制执行重新编译,因为临时表的对象ID 发生了变化
  • 如果Sybase ASE在存储的proc调用之间发现临时表的结构已更改(例如,不同的列数,不同的列名/数据类型/可空性),则Sybase ASE还将强制重新编译子proc。
  • Sybase ASE的较新版本具有一种配置,该配置(有效地)告诉编译器忽略临时表对象ID的更改,从而消除了对子proc的重新编译(注意:如果表结构发生更改,仍将进行重新编译)

回到OP的问题(子proc上的编译锁定)...

  • 是否有一些Sybase ASE行为的痕迹仍然存在于SQL Server中(从这两种产品成为豆荚的时候开始)?
  • 是否有任何SQL Server配置可以减少(消除)子proc的重新编译(如果由于对象ID的更改)?
  • OP每次可以验证父proc是否使用相同的确切结构/ DDL创建临时表吗?

至于我们考虑使用一个单一的永久表@@ SPID在会话之间区分各行的......在那里,看到... ; 重复出现的问题:

  • 如何/何时清理孤立行
  • 如果存在孤立数据(或在孤立数据的清理过程中,例如,删除@@ SPID = 10但有一个与@的新的/当前的/活动的会话,则数据库引擎重复使用@@ SPID可能导致数据准确性问题。 @ SPID = 10 =>清理会删除太多数据)
  • 从行锁到页/表锁的升级潜力
  • 如果表中有索引,则在更新索引时可能(b)锁定
  • 根据表所在的数据库,您可能正在查看用于写入日志设备的更多活动(在Sybase ASE中,可以有效地禁用tempdb中的日志记录)
  • 甚至行级(排他)锁也可以阻止其他会话(取决于隔离级别以及会话是否可以扫描/跳过所述排他锁)

我想回过头来(重新)研究根本问题(子proc上的重新编译锁),看看是否有办法减少(消除?)所说的编译锁。[不幸的是,我对这些问题的SQL Server知识是... NULL ...,所以会对一些SQL Server编译器专家的输入感兴趣。]


1
我同意,我认为需要花更多的时间来研究编译锁。这有几点要调查。 support.microsoft.com/en-us/help/263889/...
乔纳森·菲特

8

在我看来,使用@@SPID这样的方式会带来麻烦。

会话ID经常被重用;一旦用户连接注销,该会话ID便可以再次使用,并且很可能在下一个尝试连接的会话中使用。

为了使它至少半可靠地工作,您需要一个登录触发器,该触发器将使用table清除表中的先前行@@SPID。如果这样做,则使用该@@SPID列可能会在表上看到很多锁定。

SQL Server确实使用行锁定,但是它也使用页面锁定和表锁定。当然,您可以通过良好的索引来减轻这种情况,但是对我来说,这仍然像是一种反模式。

如果存储过程是用于访问受影响的表的唯一方法,则可以使用应用程序锁进行调查,从sp_getapplock本质上序列化对相关部分的访问。sp_getapplock的文档在这里Erik Darling 在这里有一篇有趣的文章。


4

是的,我确实看到了风险。使用行锁定来指望SQL是幼稚的。例如,我很确定插入和删除操作至少会使用页锁。SQL Engine根据多种因素选择锁定类型,这些因素中没有一个包含“他们的意见”。将临时表更改为表变量之类的一揽子解决方案通常也不是好主意。表变量在某些情况下非常有用,但确实存在局限性和性能问题。在大多数情况下,我更喜欢临时表。特别是当表将容纳几十行时。我将要求供应商解释为什么系统经历“繁重的编译锁”以及性能下降的原因。请记住,每当您看到时,都会发现某种“重型锁”。这并不一定意味着锁是一个问题。马克斯 关于@@ SPID的评论也很重要。此外,事务处理模型和错误处理可能是大问题。如果您的系统遇到死锁或输入数据质量问题,则标准错误处理可能会导致会话终止而没有正确重置qryTransactions表。IMO对原始问题的错误解决方法。


感谢您的回答-您的要点:我们的跟踪显示大量LCK等待存储过程InsertOrders上的编译锁定。有一个父过程Common_InsertOrders_1声明了临时表#qryTransactions,然后嵌套过程InsertOrders_2查询了临时表。此外,我们了解到还存在频繁的重新编译,因为对空的临时表进行6次修改后,任何引用该临时表的存储过程都需要重新编译
outjet

有很多原因会重新编译过程或语句。MarkP提到了许多适用于SyBase的方法,并且我确信SQL Server是类似的。临时表的行数是一个很好的例子,重新编译不一定是一件坏事。表统计信息可能会大大改变最佳查询计划,因此需要重新编译才能确定。但我想再次重申。总会有一个顶级锁,但这并不意味着该锁是一个问题。除非您看到其他有关性能问题的证据,否则不必担心。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.