2009-06-25 7 views
3

SQL Serverに対して.Net 3.5を使用していくつかのパフォーマンステストを行っています。私は100万レコードを挿入しています。これをトランザクション(直列化可能、RepeatabelReadまたはReadUncommitedのいずれか)内にラップすると、システム上で80秒未満で実行されます。トランザクションを削除すると、約300秒で実行されます。 DBMSは潜在的なロールバックを考慮する必要がないので、トランザクションを使用しないと、データベースに行を挿入する最速の方法になると思います。ここでは何が起きるのですか?これはSQL Server、SQL Server ADO.Net Provider、一般的なADO.Net、一般的なDBMSのための典型的なものですか?1Mレコードの挿入は、トランザクション内ではなく、トランザクション内でより遅いのはなぜですか?

iSeries/DB2データベースの背景があります。 DB2ではコミットメント制御とトランザクションを取得する前にジャーナリングを有効にする必要があり、ジャーナリングは比較的高価です。

私が実際にやりたかったのは、SqlCommandの挿入とvsのEntity Frameworkの挿入を比較したものですが、私はこれらの結果に非常に驚きました。

私はテストを実行するために使用するコードの下にあります。私は以下のコードを実行すると、トランザクションに包まれたすべてのコマンドは(トランザクションが明示的にsettedされていない場合)、暗黙的にあなたが1M取引を持っている、つまりので(AtStartログとAtEndログライン間で測定)約74秒

using (SqlConnection sqlConnection = new SqlConnection(connectionString)) 
{ 
    sqlConnection.Open(); 
    SqlCommand deleteCommand = new SqlCommand("DELETE FROM LockTest"); 
    deleteCommand.Connection = sqlConnection; 
    deleteCommand.ExecuteNonQuery(); 

    using (SqlTransaction transaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.Serializable)) 
    { 
     try 
     { 
      if (DEBUG) LOG.Debug("AtStart"); 

      SqlCommand insertCommand = new SqlCommand(); 
      insertCommand.Connection = sqlConnection; 
      insertCommand.Transaction = transaction; 

      insertCommand.CommandText = "INSERT INTO LockTest (Id, Name, Description, Type) " + 
       "VALUES (@id, @name, @description, @type)"; 
      SqlParameter idParameter = new SqlParameter("@id", System.Data.SqlDbType.UniqueIdentifier); 
      insertCommand.Parameters.Add(idParameter); 
      SqlParameter nameParameter = new SqlParameter("@name", System.Data.SqlDbType.NVarChar, 50); 
      insertCommand.Parameters.Add(nameParameter); 
      SqlParameter descriptionParameter = new SqlParameter("@description", System.Data.SqlDbType.NVarChar, Int32.MaxValue); 
      insertCommand.Parameters.Add(descriptionParameter); 
      SqlParameter typeParameter = new SqlParameter("@type", System.Data.SqlDbType.NChar, 20); 
      insertCommand.Parameters.Add(typeParameter); 

      insertCommand.Prepare(); 

      for (int i= 0; i < 1000000; i++) 
      { 
       Guid g = Guid.NewGuid(); 
       string s = g.ToString(); 
       insertCommand.Parameters["@id"].Value = g; 
       insertCommand.Parameters["@name"].Value = s; 
       insertCommand.Parameters["@description"].Value = DateTime.UtcNow.Ticks.ToString(); 
       insertCommand.Parameters["@type"].Value = "test"; 
       insertCommand.ExecuteNonQuery(); 
      } 
      transaction.Commit(); 
     } 
     catch 
     { 
      transaction.Rollback(); 
      throw; 
     } 

    } 
    sqlConnection.Close(); 
} 
if (DEBUG) LOG.Debug("AtEnd"); 
+1

定義により、トランザクションの分離レベルのみを読み取りに影響します。書き込み(すなわちINSERT)はすべての分離レベルで同じように動作します。 –

+0

実行ごとにテーブルがクリアされますか?一貫して毎回? – gbn

+0

DELETE FROMの代わりにTRUNCATE TABLEを使用して、テストbtwを準備することができます。そして、完全に正確であるためには、毎回ゼロからデータベースを作成し、データとログファイルを事前に拡張して(テストに十分な大きさの初期サイズを割り当てる)確認してください。実行中の1つのデータベースまたはログの増加イベントによって、その実行のすべての結果が破棄されます。 –

答えて

8

ログフラッシュ。

明示的なトランザクションがない場合、各ステートメント(つまりINSERT)によって開始された暗黙のトランザクションはコミットする必要があります。コミットは、ログ内のデータがディスクに書き込まれるまでは戻れません。つまり、各INSERT文はログディスクの書き込み操作を待たなければなりません。

明示的トランザクションはCOMMIT文が発行されるのを待つだけで、その時点までに完全なログページがすでに提出され、最後のログページにはおそらくいくつかのINSERTが含まれているため、書き込みコストは償却されます。

更新:あなたはパフォーマンスカウンタでログインフラッシュ時間を確認することができます

http://msdn.microsoft.com/en-us/library/ms189883.aspx

  • ログフラッシュ待機時間ログをフラッシュする(ミリ秒単位)合計待機時間。
  • ログフラッシュ待機数/秒ログフラッシュを待機している1秒あたりのコミット数。
  • ログフラッシュ/秒 1秒あたりのログフラッシュ回数。高速な負荷に対して
+0

これまでの情報をありがとう:私の質問の第二部分はどうですか?これはSQL Serverの典型的なものか、他のDBMSも同じように動作しますか? MySQL、Oracle? –

+0

これは、すべての先読みログベースのデータベース(http://en.wikipedia.org/wiki/Write_ahead_logging)で一般的です。 MySQLはInnoDBエンジンとOracleで同じように動作します(Oracleでこれを制御するためのあらゆる種類のノブがありますが、私はOracleの専門家ではありません)。 WALの代わりにバージョン管理されたページング(http://en.wikipedia.org/wiki/Shadow_paging)がありますが、それを展開することを認識している唯一の商用DBはInformixです。 WALやバージョン管理されたページを使用しないシステムはACIDを提供しないため、通常はトランザクションを提供しません(たとえば、MySQLのISAMエンジン)。 –

0

を取ります。少なくともsqLiteの場合

0

トランザクションでない場合は、各インサートのロックを取得して解放する必要があります。トランザクションでは、複数の挿入に対してロックを開いたままにすることができます。より少ないオーバーヘッド。

+0

取得/解放されるロックの数は、どちらの場合も同じです。 –

+0

これは、取得されたロックの細分性に依存しませんか? –

+0

@リムス:おそらくそうではありません。ロックをページ/テーブルにエスカレートします。 – gbn

関連する問題