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

ser*_*hym 5 c# t-sql oledb sql-server-2008 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();

        }

});
Run Code Online (Sandbox Code Playgroud)

如果我锁定了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();
             }

            }

    });
Run Code Online (Sandbox Code Playgroud)

假使,假设

  • 我无法改变ORM的本质:SQL不能被批量化

  • 业务规则要求在单个事务中执行交互

所以:

  • 是否有更好/更有效的方法来并行化OleDb交互?

  • 如果没有,OleDb客户端是否有可以充分利用并行性的替代方案?(也许是原生的MSSQL客户端?)

Bra*_*vic 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是根据交错而非并行执行来定义的."