我有一个vb.net应用程序,用户必须处理数百万条记录才能存储在数据库(sql CE)中。处理如下:
如果顺序完成,前两个项目占用大约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”类处理创建一个新的tasks.task,所以我认为这或多或少会创建一个连续的对象流来保存在其他线程上而不是依赖于保存每1000个对象。在“保存”类中,我有以下内容:
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
然而,这个实现比我之前的尝试慢得多,实际上似乎是同步操作。知道为什么这个方法实际上更慢?
如果有其他关于如何加快整个过程的想法,我也会喜欢反馈。
答案 0 :(得分:0)
使用TableDirect API来避免查询处理器的开销。对于插入和选择,您将看到巨大的速度增益。并且记住一个sql ce数据库是一个单独的文件,因此它可以并行地进行限制。 我最近有一些关于如何使用TableDirect apis
的deatils博客文章答案 1 :(得分:-1)
Tasks.Task.Factory.StartNew
不一定会创建新的后台线程。它创建了一个可以异步执行的任务。相反,您是否想要使用Parallel.Invoke
?