【发布时间】:2016-04-20 07:25:13
【问题描述】:
我偶然发现了一个非常奇怪的案例。我们有一个 SQL Server 2012 数据库和这样一个表
CREATE TABLE [dbo].[ActiveTransactions]
(
[Id] [BIGINT] IDENTITY(1,1) NOT NULL,
[Amount] [DECIMAL](12, 4) NOT NULL,
[TypeId] [SMALLINT] NOT NULL,
[GameProviderId] [SMALLINT] NULL,
[UserId] [INT] NOT NULL,
[Checksum] [NVARCHAR](150) NOT NULL,
[Date] [DATETIME2](7) NOT NULL,
[ExternalKey] [VARCHAR](60) NULL,
[ExternalDescription] [NVARCHAR](1000) NULL,
[OperatorId] [SMALLINT] NULL,
[GameId] [NVARCHAR](50) NULL
)
这个表有多个索引,但我想在这里讨论的两个是PK_ActiveTransactions(主键,聚集):
ALTER TABLE [dbo].[ActiveTransactions]
ADD CONSTRAINT [PK_ActiveTransactions]
PRIMARY KEY CLUSTERED ([Id] DESC)
和IX_ActiveTransactions_UserIdAmount(非集群,非唯一):
CREATE NONCLUSTERED INDEX [IX_ActiveTransactions_UserIdAmount]
ON [dbo].[ActiveTransactions] ([UserId] ASC, [Id] DESC)
INCLUDE ([Amount])
有一个查询依赖于我的解决方案的主要部分,并在某个进程启动时被调用。基本上每次在我的代码端调用SomeMethod 时,它都会启动 SQL 事务,然后执行过程(如下所示),从而锁定它选择的条目,然后计算一些东西并在该表中插入新行并提交交易。锁定过程执行此 SQL 语句
SELECT TOP 1
id ,
Amount ,
TypeId ,
GameProviderId ,
UserId ,
[Checksum] ,
[Date] ,
ExternalKey
FROM ActiveTransactions WITH ( UPDLOCK )
WHERE @UserId = UserId
ORDER BY Id DESC
现在情况就是这样。当我查看此表中的一些条目时,似乎有多个(同时请求的)条目为相同的@UserId 选择了相同的条目。确切地说,有 5 个新条目(当时请求,因为它们具有在代码端计算的完全相同的 [Date] 值),它们都选择了相同的条目,然后重新计算了一些东西(所有 5 个都计算了同样的事情)并同时插入5个新行,而不是一一插入(这应该是由SELECT查询末尾的WITH(UPDLOCK)语句引起的,我相信)。
然后我尝试做这样的事情,我打开了三个新的查询窗口,我在一个窗口中使用BEGIN TRAN 命令启动了一个事务,然后在上面的 SELECT 语句中执行,在其他两个窗口中我做了同样的事情,当我提交第一个语句,第二个查询在提交第二个语句之后立即获取它,第三个语句获取它。 (一切都按预期工作),在查询末尾添加WITH INDEX(INDEX_NAME)(UPDLOCK 仍然存在)后,事情开始看起来很奇怪。对于第一个选择,我指定了WITH INDEX(PK_ActiveTransactions)(主键),对另外两个我指定了WITH INDEX(IX_ActiveTransactions_UserIdAmount)。在运行所有 3 个命令后,加上与第一个命令在同一个表中的 INSERT,(第二个和第三个仍在等待第一个命令完成)但是当我提交第一个命令时,第二个获取旧条目和第三个同时获得了新条目。我认为这种行为可能导致了上面解释的错误,但这怎么可能呢?
SQL Server 是否会同时对同一个查询使用两个不同的执行计划(因此使用不同的索引)?该表在一天结束时达到 10-15 百万个条目,但每天早上大约 6 点执行的作业只剩下 1-2 百万行。这会导致 SQL Server 意外切换索引吗?但无论如何,我认为这是一个系列问题,这意味着即使在提交之后,索引也可能不包含已提交的数据。
上述问题只发生过几次,我能够识别它们发生了两次
【问题讨论】:
-
索引很可能使用行锁,而 PK 将使用表锁。
-
@SeanPearce 在这种情况下不同的数据来自哪里?你的意思是说用PK从表中选择一个条目会锁定整个表?
-
PK 在 id 上,搜索是通过 userid,所以锁的行为会有所不同。如果没有索引来定位要锁定的行,所有测试的行都会被锁定,并且对符合条件的行的锁定会一直保持到事务完成为止。
标签: sql-server database indexing locking sql-execution-plan