sql-server – 在并发更新期间选择查询跳过记录
我有N个线程并发处理的表.
CREATE TABLE [dbo].[Jobs] ( [Id] BIGINT NOT NULL CONSTRAINT [PK_Jobs] PRIMARY KEY IDENTITY,[Data] VARBINARY(MAX) NOT NULL,[CreationTimestamp] DATETIME2(7) NOT NULL,[Type] INT NOT NULL,[ModificationTimestamp] DATETIME2(7) NOT NULL,[State] INT NOT NULL,[RowVersion] ROWVERSION NOT NULL,[Activity] INT NULL,[Parent_Id] BIGINT NULL ) GO CREATE NONCLUSTERED INDEX [IX_Jobs_Type_State_RowVersion] ON [dbo].[Jobs]([Type],[State],[RowVersion] ASC) WHERE ([State] <> 100) GO CREATE NONCLUSTERED INDEX [IX_Jobs_Parent_Id_State] ON [dbo].[Jobs]([Parent_Id],[State] ASC) GO 作业正在添加到状态= 0(新)的表 – 它可以被这个状态的任何工作人员使用.当工作者获得此队列项时,状态更改为50(处理),作业变为不可用于其他用户(工作人员调用[dbo].[Jobs_GetFirstByType]与参数:Type = any,@ CurrentState = 0,@ NewState = 50). CREATE PROCEDURE [dbo].[Jobs_GetFirstByType] @Type INT,@CurrentState INT,@NewState INT AS BEGIN SET TRANSACTION ISOLATION LEVEL READ COMMITTED; DECLARE @JobId BIGINT; BEGIN TRAN SELECT TOP(1) @JobId = Id FROM [dbo].[Jobs] WITH (UPDLOCK,READPAST) WHERE [Type] = @Type AND [State] = @CurrentState ORDER BY [RowVersion]; UPDATE [dbo].[Jobs] SET [State] = @NewState,[ModificationTimestamp] = SYSUTCDATETIME() OUTPUT INSERTED.[Id],INSERTED.[RowVersion],INSERTED.[Data],INSERTED.[Type],INSERTED.[State],INSERTED.[Activity] WHERE [Id] = @JobId; COMMIT TRAN END 处理完成后,作业状态可以再次更改为0(新建),也可以将其设置为100(已完成). CREATE PROCEDURE [dbo].[Jobs_UpdateStatus] @Id BIGINT,@State INT,@Activity INT AS BEGIN UPDATE j SET j.[State] = @State,j.[Activity] = @Activity,j.[ModificationTimestamp] = SYSUTCDATETIME() OUTPUT INSERTED.[Id],INSERTED.[RowVersion] FROM [dbo].[Jobs] j WHERE j.[Id] = @Id; END 作业具有层次结构,只有当所有子项完成时,父作业才会获得State = 100(Completed). CREATE PROCEDURE [dbo].[Jobs_GetCountWithExcludedState] @ParentId INT,@ExcludedState INT AS BEGIN SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT COUNT(1) FROM [dbo].[Jobs] WHERE [Parent_Id] = @ParentId AND [State] <> @ExcludedState END 这个存储过程的主要问题是奇怪的行为.有时候,为父工作返回0,但它完全没有完成工作.我尝试打开更改数据跟踪和一些调试信息(包括分析) – 子作业100%没有状态= 100,当SP返回0. UPD: using (var ts = new TransactionScope()) { _jobManager.AddChilds(parentJob); parentJob.State = 0; parentJob.Activity = 30; // in this activity worker starts checking child jobs ts.Complete(); } 解决方法如果实际上你的程序Jobs_GetCountWithExcludedState在事实上符合您的条件的提交记录时返回0个记录的数量将是非常令人不安的.这是一个非常简单的过程.所以有两种可能性:>由于SQL Server或数据的问题,查询失败 腐败是不可能的,但可能的原因.您可以使用DBCC CHECKDB检查是否存在损坏. 最有可能的是,实际上没有任何一个Parent_ID等于@ParentId参数并且在运行时处于100状态的作业记录. 我强调承诺,因为这是交易将会看到的. 你从来没有真正解释你的问题如何在工作上设置Parent_ID.我的第一个想法是,也许你正在检查未处理的子作业,它没有找到,但是另一个进程将其添加为另一个不完整作业的Parent_ID.这是可能吗? 我看到你添加了一个更新,以显示当您添加一个子作业记录时,父和子记录的更新被包装在事务中.这是好的,但不是我问的问题.这是我正在考虑的一种可能性: >为父母插入并提交作业记录. 我不是说这是发生了什么…我只是问是否可能,并采取什么步骤来防止它?例如,在您的问题更新中的上述代码中,您选择一个ParentJob将该子项附加到事务外部.可能是您正在选择父作业,然后在运行将子添加到父项的事务之前完成它?或者也可能是父作业的最后一个子作业完成,以便工作线程检查并标记父项完成,但是一些其他工作线程已经选择作为新的子作业的父级作业? 有许多不同的情况可能会导致你所描述的症状.我相信这个问题是在你没有与我们分享的代码中找到的,特别是关于如何创建作业以及调用Jobs_GetCountWithExcludedState的代码.如果你能给出更多的信息,我想你会更有可能找到一个可用的答案,否则我们可以做的最好的事情是猜测在代码中可能发生的所有事情,我们看不到. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |