Boo*_*Boo 14 t-sql sql-server concurrency locking sql-server-2008
我有由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
Run Code Online (Sandbox Code Playgroud)
作业正在添加到表中State=0 (New)- 它可以被此状态下的任何工作者使用.当worker获取此队列项时,State更改为,50 (Processing)并且其他使用者无法使用该作业(工作程序[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
Run Code Online (Sandbox Code Playgroud)
处理完成后,State可以0 (New)再次将作业更改为或者可以将作业设置为100 (Completed).
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
Run Code Online (Sandbox Code Playgroud)
乔布斯具有层次结构,State=100 (Completed)只有当所有子项都完成后才能获得父作业.一些工作者调用存储过程([dbo].[Jobs_GetCountWithExcludedState]with @ExcludedState=100)返回未完成的作业数,当它返回0时,父作业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
Run Code Online (Sandbox Code Playgroud)
主要问题是此存储过程的奇怪行为.有时它为父作业返回0,但它确实有未完成的作业.我尝试打开更改数据跟踪和一些调试信息(包括分析) - State=100当SP返回0时,100%没有子作业.似乎SP跳过记录,不在100 (Completed)状态,但为什么会发生以及我们如何可以防止这个吗?
UPD:[dbo].[Jobs_GetCountWithExcludedState]当父作业有孩子时,
呼叫开始.当工作人员在没有存在的情况下开始检查子作业时,不存在任何情况,因为创建子项并设置为包含在事务中的父作业检查活动:
using (var ts = new TransactionScope())
{
_jobManager.AddChilds(parentJob);
parentJob.State = 0;
parentJob.Activity = 30; // in this activity worker starts checking child jobs
ts.Complete();
}
Run Code Online (Sandbox Code Playgroud)
如果实际上您的程序Jobs_GetCountWithExcludedState返回 0 条记录,而实际上有符合您的条件的已提交记录,那么这将是非常令人不安的。这是一个非常简单的过程。所以有两种可能:
腐败是一个不太可能但可能的原因。您可以使用DBCC CHECKDB检查损坏情况。
最有可能的是,实际上没有已提交的作业记录,其值Parent_ID等于该@ParentId参数,并且在运行时不处于 100 的状态。
我强调承诺,因为这就是交易将看到的。
您在问题中从未真正解释过Parent_ID工作是如何设置的。我的第一个想法是,也许您正在检查未处理的子作业,但它没有找到,但另一个进程将其添加为Parent_ID另一个未完成的作业。这有可能吗?
我看到您添加了一个更新,以表明当您添加子作业记录时,父记录和子记录的更新都包含在事务中。这很好,但不是我问的问题。这是我正在考虑的一种可能性:
Jobs_GetFirstByType抢了父母的工作。Jobs_UpdateStatus并将其状态更新为 100。Jobs_GetCountWithExcludedState随作业调用并返回 0。我并不是说这就是正在发生的事情......我只是问这是否可能以及您正在采取哪些措施来防止它?例如,在上面更新问题的代码中,您选择将ParentJob孩子附加到交易之外。是否可能是您正在选择一个父作业,然后在运行将子作业添加到父作业的事务之前它已完成?或者也许父作业的最后一个子作业完成,因此工作线程检查并将父作业标记为完成,但其他一些工作线程已经选择该作业作为新子作业的父作业?
有许多不同的情况可能会导致您所描述的症状。我相信问题出在您尚未与我们共享的一些代码中,特别是关于如何创建作业以及围绕调用的代码Jobs_GetCountWithExcludedState。如果您可以提供更多信息,我认为您将更有可能找到可用的答案,否则我们能做的最好的事情就是猜测我们看不到的代码中可能发生的所有事情。
| 归档时间: |
|
| 查看次数: |
321 次 |
| 最近记录: |