2012-07-10 70 views
3

我有以下方法在表中批量插入數據。 首先我的代碼填充數據表中的數據,並使用.net的SqlBulkCopy claas將這些數據插入相應的表中。如何通過不同的.net(c#)線程處理多個SQL事務

我有要求,數據應插入所有表或兩者都沒有。 爲此,我使用了.net的SqlTransaction類。

場景是,多個線程同時執行下面的代碼塊。

public void Import() 
     { 
      using (SqlConnection sqlConnection = new SqlConnection(connectionString)) 
      { 

       SqlTransaction sqlTrans =null; 
       try 
       { 
        sqlConnection.Open(); 
        sqlTrans = sqlConnection.BeginTransaction(IsolationLevel.Serializable) 


        SqlCommand cmd = sqlConnection.CreateCommand(); 
        cmd.CommandText = "select top 1 null from lockTable with(xlock)"; 
        cmd.CommandTimeout = 3600*3; 
        cmd.Transaction = sqlTrans; 
        SqlDataReader reader = cmd.ExecuteReader(); 


        foreach (DataTable dt in DataTables) 
        { 
         ImportIntoDatabase(sqlConnection, dt, sqlTrans); 
        } 

        reader.Close(); 
        sqlTrans.Commit();      
       } 
       catch (Exception ex) 
       { 
        sqlTrans.Rollback(); 
        throw ex; 
       } 
      } 
     } 

     private void ImportIntoDatabase(SqlConnection sqlConn, DataTable dt, SqlTransaction sqlTrans) 
     { 
      using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.Default, sqlTrans)) 
      { 
       bulkCopy.BulkCopyTimeout = dt.Rows.Count * 10; 
       try 
       { 
        bulkCopy.DestinationTableName = dt.TableName;      
        bulkCopy.WriteToServer(dt); 
       } 
       catch (Exception ex) 
       { 
        throw ex; 
       } 
      } 
     } 

爲了處理這種併發性,我已創建了一個虛擬表(命名爲「lockTable」表),在數據庫中,其中所述其它表駐留(大容量插入表)。我在SqlTransaction中的這個虛擬表上獲得獨佔鎖,命令超時3個小時。

問題: 我得到以下異常

:無法訪問目標表「TBL1」(TBL1是批量插入表),其次是另一個異常

,而回滾在catch塊交易

:執行活動時出錯服務器無法恢復事務。說明:3a00000001。 此會話中處於活動狀態的事務已被另一個會話提交或中止。

任何人都可以幫助我這個奇怪的代碼行爲。在互聯網上我已經在這個問題上搜索了很多,但是我沒有找到對我有幫助的東西。

+0

所做的更改。如果我理解這個權利,你處理的併發由數據庫鎖定表。所以只有1個線程有效地導入數據。爲什麼各種線程呢? – smp 2012-07-10 07:52:20

+0

如果我明白這個權利,您可以通過在數據庫中鎖定一個表來處理併發性。所以只有1個線程有效地導入數據。爲什麼各種線程呢? 無論如何,我認爲應該更有效地處理程序中的併發,或者用一個Mutex或一個Semaphore來發信號代碼,或者在一個ThreadPool中執行這個代碼而不使用表鎖定來有效地同時執行這些輸入。 (我編輯以前的評論太慢了,對不起) – smp 2012-07-10 07:58:49

回答

0

我解決了我的問題。

以下是我已經對我的導入方法

public void Import() 
     { 
      using (SqlConnection sqlConnection = new SqlConnection(connectionString)) 
      { 
       sqlConnection.Open(); 
       using (SqlTransaction sqlTrans = sqlConnection.BeginTransaction()) 
       { 
        try 
        {      
         SqlCommand cmd = sqlConnection.CreateCommand(); 
         cmd.CommandText = "select top 1 null from lockTable with(xlock)"; 
         cmd.CommandTimeout = LOCK_TIME_OUT; 
         cmd.Transaction = sqlTrans; 
         SqlDataReader reader = cmd.ExecuteReader(); 


         foreach (DataTable dt in DataTables) 
         { 
          ImportIntoDatabase(sqlConnection, dt, sqlTrans);        
         } 

         reader.Close(); 
         sqlTrans.Commit();       
        } 
        catch (Exception ex) 
        { 
         sqlTrans.Rollback(); 
         throw ex; 
        } 
       } 
       sqlConnection.Close(); 
      } 
     } 
0

如果多個線程有權訪問「導入」方法,那麼您不應該鎖定此方法的內容?

我不認爲你需要一個虛擬表,你只需要鎖定上面的兩個方法。

我還想提一下,你應該加入所有的線程,這樣你就可以知道它們何時完成。

2

在導入(數據表中的DataTable dt)不會是線程安全的。

sqlConnection已從導入中擁有一個活動的讀取器,因此無法在ImportIntoDatabase中使用連接。

Echo smp - 如果你正在鎖定一個表,那麼爲什麼多線程?

如果要在SQL插入發生時建立輸入,請使用 Asynch方法,如SqlCommand.BeginExecuteReader。沒有線程的開銷,你會得到異步。而數據表相對較慢。我插入使用TVP和輕量級對象。插入性能的一個重要因素是索引碎片。如果儘可能按照聚集索引的順序插入順序。循環是簡單的構建輸入,等待異步,運行asych。或者構建輸入可能是從隊列中讀取的輸入。 SQL插入到同一個表的速度通常並不會更快。我的經驗是有序的插入,插入之間無間隙。