VB.net 具有许多数据库调用的多线程

VB.net Multithreading with many database calls

我有一个 vb.net 应用程序,其中用户必须处理数百万条记录才能存储在数据库中 (sql CE)。处理过程如下:

  1. 从数据库中检索一些保存的数据来实例化一个对象。
  2. 执行一些计算。
  3. 将计算出的对象数据保存到数据库中不同的table。

如果按顺序完成,前两项大约需要 30% 的时间,最后一项大约需要 70% 的时间。

我认为将大部分数据库写入更像是在单独线程上的批处理可能是有益的,希望将成本降低(在理想情况下)执行项目 1 和 2 所需的 30% . 我尝试通过将处理过的对象存储在一个列表中来做到这一点,每当列表计数超过一定数量时在单独的线程上调用一个动作,我就保存数据。每次保存数据时,我实际上是为主对象和几个相关的子对象保存数据,即

cmd.CommandText = "INSERT INTO [MainObjectTable] (Data1, Data2, Data3) VALUES ( @Data1, @Data2, @Data3)"
cmd.ExecuteNonQuery()

cmd2.CommandText = "SELECT MAX(idx) FROM [MainObjectTable]"
MainObjectIdx = CInt(cmd2.ExecuteScalar())

'Loop over child objects and save each one to database'
cmd3.CommandText = "INSERT INTO [ChildObject] (MainObjectIdx, Data4, Data5, Data6) VALUES ( MainObjectIdx, @Data4, @Data5, @Data6 )"

for i = 0 to ChildList.Count -1
     [Code to Update parameters for the command object]
     cmd3.ExecuteNonQuery()
next

我锁定了数据库以防止同时保存多条记录。我必须这样做(我认为),因为我使用主对象的记录键来进一步处理子对象的数据,如上所示。

代码基本上是这样的:

Private sub CalcData()
    Dim objectList as new List(of ParentObject)
    For k = 0 to Calculations
       'Load data, instantiate object'
       Dim o as new ParentObject(IDs(k)) '<-- This pulls records from the sql ce database and loads the object'
       o.calcData  'calculate the new data based on loaded data'
       objectList.add(o) 'store in a list'
       If objectList.Count > 1000 then
            Batchsave(objectList)
            objectList.clear()
       End If
    Next
End Sub

Private Sub BatchSave(objList As List(of ParentObject))                                          
    mTaskList.Add(Tasks.Task.Factory.StartNew(
            Sub()
                DBLock.EnterWriteLock()
                Try
                  for j = 0 to objectList.count-1
                    [Code to update command object parameters, and save the object (and children) as discussed above where I show the sql statements]
                  next
                Finally
                  DBLock.ExitWriteLock()
                End Try
            End Sub))              
End Sub

我认为这个方案可以最大化性能,允许在后台线程上完成数据保存。我将保存结构化为一个批处理过程(一次 1000 条记录),因为我读到在更新许多记录时参数化 sql 更有效。但是时间的减少并不是非常令人印象深刻。

我还尝试创建一个新的 "Save" class,我将数据传递给它以在数据可用时保存。 "Save" class 每次将父对象传递给它时都会处理创建一个新的 tasks.task ,所以我认为这或多或少会创建一个连续的对象流以保存在其他线程上而不是而不是依靠每 1000 个对象保存一次。在 "Save" class 里面我有以下内容:

Public Class SaveData

Public Sub SaveBDLItem(ByVal o As ParentObject)

    Tasks.Task.Factory.StartNew(
            Sub()
                Dim Object   
                mParentLock.EnterWriteLock()
                Try
                    mcmd1.Parameters.Clear()

                    [code to add parameters to command object]

                    mcmd1.ExecuteNonQuery()
                    'get new key '
                    objectIDx= CInt(mcmd2.ExecuteScalar())
                Finally
                    mBDLLock.ExitWriteLock()
                End Try

                'Now update children'
                mChildLock.EnterWriteLock()
                Try
                    For j = 0 To ParentObject.Children.Count - 1
                        mcmd3.Parameters.Clear()

                        [code to add parameters to command object]

                        mcmd3.ExecuteNonQuery()

                    Next
                Finally
                    mChildLock.ExitWriteLock()
                End Try

            End Sub))

End Sub
.
.
.
End Class

然而,这个实现比我之前的尝试慢了很多,而且实际上似乎是同步运行的。知道为什么这种方法实际上更慢吗?

如果有其他关于如何加快整个过程的想法,我也很乐意提供反馈。

Tasks.Task.Factory.StartNew 不一定创建新的后台线程。它创建了一个可以异步执行的任务。相反,您想使用 Parallel.Invoke?

使用 TableDirect API 避免查询处理器的开销。对于插入和选择,您将看到巨大的速度提升。请记住 sql ce 数据库是单个文件,因此您可以并行执行的操作非常有限。 我最近有很多博客文章详细介绍了如何使用 TableDirect api