在共享的OleDbConnection / OleDbTransaction中并行执行EntonNonQuery

时间:2011-10-09 18:41:42

标签: c# tsql sql-server-2008 oledb task-parallel-library

发现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客户端?)

4 个答案:

答案 0 :(得分:6)

交易需要是ACID,但“耐久性”只需在交易结束时执行。因此,在明显的SQL语句执行后,实际在后台完成,而您的事务处理其他语句时,可能会推迟到磁盘的物理IO。

因此,发出SQL语句串行可能并不比发布并发慢得多。请考虑以下情况:

  • 执行写入数据的SQL语句[A]。磁盘实际上没有被触及,写入只是稍后排队等待,因此执行流程很快返回到客户端(即[A]不会长时间阻塞)。
  • 执行写入数据的SQL语句[B]。写入排队,[B]不会像以前那样长时间阻塞。此时[A]的物理I / O可能已经在后台发生。
  • 其他处理在事务中进行,而DBMS在后台执行磁盘的物理I / O.
  • 交易已提交。
    • 如果排队的写入完成,则无需等待。
    • 如果现在没有完成排队写入,请等到它们。顺便说一句,有些数据库可以放宽“持久性”要求以避免这种等待,但不能放松MS SQL Server(AFAIK)。

当然有些情况下DBMS的这种“自动并行性”不能很好地工作,例如当有一个WHERE子句时,不同的语句会触及不同磁盘上的不同分区 - DBMS会喜欢并行化这些条款,但如果它们一个接一个地供给它就不能。

无论如何,不​​要猜测你的性能瓶颈在哪里。测量它!


BTW,MARS不会帮助你并行化你的陈述 - 根据MSDN“但请注意,MARS是根据交错而非并行执行来定义的。”

答案 1 :(得分:1)

  

发现OleDBConnection似乎不是ThreadSafe。

是的,这符合documentation

  

此类型的任何公共静态(在Visual Basic中为Shared)成员都是   线程安全。任何实例成员都不能保证是线程   安全

因此,只需在线程内创建连接,并让底层OLE DB提供程序处理连接池。此外,如果你有可能,绝对摆脱OleDbConnection并使用相应的ADO.NET驱动程序为您的数据库,除非您运行一些非常奇特的数据库,应该有一个ADO.NET驱动程序。

答案 2 :(得分:1)

由于它不是线程安全的,因此将Parallel.ForEach更改为正常foreach并按顺序执行。它的工作速度要慢于完全没有。

答案 3 :(得分:0)

要获得最大的性能提升,请在Parallel.ForEach中打开一个新连接。这样,您将拥有与数据库的真正并行连接。

确保已启用连接池并正确设置了最小和最大连接属性。

尝试这种方法并使用秒表类来计算不同方法之间的性能,并选择最适合您的方法。这取决于您将对数据库和架构执行的查询类型。