发现OleDBConnection似乎不是ThreadSafe。它似乎试图打开多个连接。
//doesn't work
using (OleDbConnection oConn = TheDataAccessLayer.GetConnection())
using (OleDbTransaction oTran = oConn.BeginTransaction())
Parallel.ForEach(ORMObjects, (ORMObject, State) =>
{
if (!State.ShouldExitCurrentIteration && !State.IsExceptional)
{
var Error = ORMObject.SomethingThatExecutesANonQuery(oConn,oTran)
if (Error.Number != 0)
State.Stop();
}
});
如果我锁定了ExecuteNonQuery的连接,那么错误就会消失,但性能却会消失。
//works
using (OleDbConnection oConn = TheDataAccessLayer.GetConnection())
using (OleDbTransaction oTran = oConn.BeginTransaction())
Parallel.ForEach(ORMObjects, (ORMObject, State) =>
{
if (!State.ShouldExitCurrentIteration && !State.IsExceptional)
{
lock(oConn)
{
var Error = ORMObject.SomethingThatExecutesANonQuery(oConn,oTran)
if (Error.Number != 0)
State.Stop();
}
}
});
假设
我无法改变ORM的本质:SQL不可能 膨体
业务规则要求在单个事务中执行交互
所以:
是否有更好/更有效的方法来并行化OleDb互动?
如果没有,OleDb客户端是否有可以充分利用并行性的替代方案? (也许是原生的MSSQL客户端?)
答案 0 :(得分:6)
交易需要是ACID,但“耐久性”只需在交易结束时执行。因此,在明显的SQL语句执行后,实际在后台完成,而您的事务处理其他语句时,可能会推迟到磁盘的物理IO。
因此,发出SQL语句串行可能并不比发布并发慢得多。请考虑以下情况:
当然有些情况下DBMS的这种“自动并行性”不能很好地工作,例如当有一个WHERE
子句时,不同的语句会触及不同磁盘上的不同分区 - DBMS会喜欢并行化这些条款,但如果它们一个接一个地供给它就不能。
无论如何,不要猜测你的性能瓶颈在哪里。测量它!
答案 1 :(得分:1)
发现OleDBConnection似乎不是ThreadSafe。
是的,这符合documentation:
此类型的任何公共静态(在Visual Basic中为Shared)成员都是 线程安全。任何实例成员都不能保证是线程 安全
因此,只需在线程内创建连接,并让底层OLE DB提供程序处理连接池。此外,如果你有可能,绝对摆脱OleDbConnection并使用相应的ADO.NET驱动程序为您的数据库,除非您运行一些非常奇特的数据库,应该有一个ADO.NET驱动程序。
答案 2 :(得分:1)
由于它不是线程安全的,因此将Parallel.ForEach
更改为正常foreach
并按顺序执行。它的工作速度要慢于完全没有。
答案 3 :(得分:0)
要获得最大的性能提升,请在Parallel.ForEach中打开一个新连接。这样,您将拥有与数据库的真正并行连接。
确保已启用连接池并正确设置了最小和最大连接属性。
尝试这种方法并使用秒表类来计算不同方法之间的性能,并选择最适合您的方法。这取决于您将对数据库和架构执行的查询类型。