单个临时表上的并发更新

时间:2018-07-11 12:34:32

标签: sql-server vb.net concurrency threadpool updates

我正在开发一个服务应用程序(VB.NET),该服务应用程序从源中提取信息并将其导入到SQL Server数据库中

该过程一次可能涉及一个或多个“批次”信息(任何给定的“运行”中的批次数量和大小基于在其他地方维护的队列都是任意的)

为每个批次分配一个标识符(BatchID),以便可以轻松地识别登台表中属于该批次的记录集

每个批次的ETL过程本质上是顺序的;将原始数据批量插入到临时表中,然后一系列存储过程对许多列执行更新,直到准备好导入数据为止

这些存储过程由服务依次调用,通常是简单的UPDATE命令

每个SP都将BatchID作为输入参数,并将其指定为包含在每个UPDATE中的条件,例如:

UPDATE dbo.stgTable
SET FieldOne = (CASE
                    WHEN S.[FieldOne] IS NULL
                        THEN T1.FieldOne
                    ELSE
                        S.[FieldOne]
                END
                )
, FieldTwo =   (CASE
                    WHEN S.[FieldTwo] IS NULL
                        THEN T2.FieldTwo
                    ELSE
                        S.[FieldTwo]
                END
                )
FROM dbo.stgTable AS S
LEFT JOIN dbo.someTable T1 ON S.[SomeField] = T1.[SomeField]
LEFT JOIN dbo.someOtherTable T2 ON S.[SomeOtherField] = T2.[SomeOtherField]
WHERE S.BatchID = @BatchID

一些SP也引用了函数(标量和表值),并且都合并了TRY / CATCH结构,因此我可以从输出参数中判断某个SP是否失败

最终的SP是MERGE操作,用于将丰富的数据从登台表移至生产表(同样,特定于所提供的BatchID)

我想在服务中使用此流程,以便大批处理不会在同一运行中容纳小批处理

我认为这应该没有问题,因为没有线程可以尝试处理登台表中的记录,而该记录可以被另一个线程作为目标(没有竞争条件)

但是,我注意到,当我对进程进行线程化时,任意批处理上的任意步骤似乎都失败了(但是从SP的输出中未记录任何错误)

失败是不一致的;例如有时第2、3和5批次将失败(分别在SP的3、5和7上),其他时候将是不同的批次,每个批次按顺序的不同步骤

当我按顺序导入批次时,它们都可以完美地导入–总是如此!

我无法确定这是否是服务端(VB.NET)的问题-例如是每个线程打开与数据库的独立连接,还是它们可以共享同一线程(我将其设置为每个应该应该独立...”

或者如果问题出在SQL Server方面-例如并发SP调用操作同一表上的数据是否可行,即使如上所述,没有线程/批处理将接触到属于另一个线程/批处理的记录

(在这一点上-我尝试使用CTE来基于BatchID从临时表中创建数据的子集,并将UPDATE应用于这些数据,但是确实发生了相同的行为)

WITH CTE AS (
             SELECT *
             FROM dbo.stgTable
             WHERE BatchID = @BatchID
            )
UPDATE CTE...

也许是问题在于多个SP同时调用相同的功能,这就是为什么其中一个或多个失败的原因(我不明白为什么这会成为问题?)

我们将非常感激收到任何建议–我整周都在忙碌着,我无法一生确切地确定问题可能出在哪里!

更新以包含示例服务代码

这是服务类中启动线程的代码

For Each ItemInScope In ScopedItems
    With ItemInScope 
        _batches(_batchCount) = New Batch(.Parameter1, .Parameter2, .ParameterX)
        With _batches(_batchCount)
            If .Initiate() Then
                _doneEvents(_batchCount) = New ManualResetEvent(False)
                Dim _batchWriter = New BatchWriter(_batches(_batchCount), _doneEvents(_batchCount))
                ThreadPool.QueueUserWorkItem(AddressOf _batchWriter.ThreadPoolCallBack, _batchCount)
            Else
                _doneEvents(_batchCount) = New ManualResetEvent(True)
            End If
        End With
    End With
    _batchCount += 1
Next

WaitHandle.WaitAll(_doneEvents)

这是BatchWriter类

Public Class BatchWriter

    Private _batch As Batch
    Private _doneEvent As ManualResetEvent

    Public Sub New(ByRef batch As Batch, ByVal doneEvent As ManualResetEvent)
        _batch = batch
        _doneEvent = doneEvent
    End Sub

    Public Sub ThreadPoolCallBack(ByVal threadContext As Object)
        Dim threadIndex As Integer = CType(threadContext, Integer)
        With _batch
            If .PrepareBatch() Then
                If .WriteTextOutput() Then
                    .ProcessBatch()
                End If
            End If
        End With
        _doneEvent.Set()
    End Sub

End Class

Batch类的PrepareBatch和WriteTextOutput函数完全包含在服务应用程序中-只有ProcessBatch函数使服务开始与数据库进行交互(通过实体框架)

这是功能

Public Sub ProcessScan()
    ' Confirm that a file is ready for import
    If My.Computer.FileSystem.FileExists(_filePath) Then
        Dim dbModel As New DatabaseModel
        With dbModel
            ' Pass the batch to the staging table in the database
            If .StageBatch(_batchID, _filePath) Then
                ' First update (results recorded for event log)
                If .UpdateOne(_batchID) Then
                    _stepOneUpdates = .RetUpdates.Value
                    ' Second update (results recorded for event log)
                    If .UpdateTwo(_batchID) Then
                        _stepTwoUpdates = .RetUpdates.Value
                        ' Third update (results recorded for event log)
                        If .UpdateThree(_batchID) Then
                            _stepThreeUpdates = .RetUpdates.Value

                            ....
End Sub

0 个答案:

没有答案