.NET批量大数据插入性能分析及比较(3.使用事务)

来源:互联网 发布:数据 战略资源 编辑:程序博客网 时间:2024/06/15 19:03

[c-sharp] view plaincopy
  1. #region 拼接sql语句并使用Transaction  
  2.  public static bool ExecuteTransactionInsert(DataTable dt, int batchSize)  
  3.  {  
  4.      int count = dt.Rows.Count;  
  5.      StringBuilder sql = new StringBuilder(220);  
  6.      bool flag = false;  
  7.      SqlConnection cn = null;  
  8.      SqlCommand cmd = null;  
  9.      try  
  10.      {  
  11.          cn = new SqlConnection(connectionString);  
  12.          cmd = new SqlCommand();  
  13.          cmd.Connection = cn;  
  14.          cn.Open();  
  15.          for (int i = 0; i < count; i += batchSize)  
  16.          {  
  17.              sql.Append("begin try;begin tran;");  
  18.              for (int j = i; j < i + batchSize && j < count; j++)  
  19.              {  
  20.                  sql.AppendFormat("Insert into TestTable(Id, Name) Values({0}, '{1}');", dt.Rows[j]["Id"], dt.Rows[j]["Name"]);  
  21.              }  
  22.              sql.Append("commit tran;end try/nbegin catch/nrollback tran/n end catch");  
  23.              //LogHelper.Info(sql.ToString());  
  24.              cmd.CommandText = sql.ToString();  
  25.              cmd.ExecuteNonQuery();  
  26.              sql.Clear();  
  27.          }  
  28.          flag = true;  
  29.      }  
  30.      catch (Exception ex)  
  31.      {  
  32.          LogHelper.Error(sql.ToString(), ex);  
  33.          return false;  
  34.      }  
  35.      finally  
  36.      {  
  37.          if (cn != null)  
  38.          {  
  39.              if (cn.State == ConnectionState.Open)  
  40.              {  
  41.                  cn.Close();  
  42.              }  
  43.              cn.Dispose();  
  44.          }  
  45.          if (cmd != null) cmd.Dispose();  
  46.      }  
  47.      return flag;  
  48.  }  
  49.  #endregion  

 

结果如下:

Use SqlServer Batch Transaction Insert;RecordCount:40000;BatchSize:10;Time:24979;

Use SqlServer Batch Transaction Insert;RecordCount:40000;BatchSize:20;Time:7669;

Use SqlServer Batch Transaction Insert;RecordCount:40000;BatchSize:50;Time:5512;

Use SqlServer Batch Transaction Insert;RecordCount:40000;BatchSize:100;Time:5018;

Use SqlServer Batch Transaction Insert;RecordCount:40000;BatchSize:200;Time:4557;

[c-sharp] view plaincopy
  1. #region 拼接sql语句并使用SqlTransaction  
  2.         public static bool ExecuteSqlTransactionInsert(DataTable dt, int batchSize)  
  3.         {  
  4.             int count = dt.Rows.Count;  
  5.             StringBuilder sql = new StringBuilder(220);  
  6.             bool flag = false;  
  7.             SqlConnection cn = null;  
  8.             SqlCommand cmd = null;  
  9.             SqlTransaction tran = null;  
  10.             try  
  11.             {  
  12.                 cn = new SqlConnection(connectionString);  
  13.                 cmd = new SqlCommand();  
  14.                 cmd.Connection = cn;  
  15.                 cn.Open();  
  16.   
  17.                 for (int i = 0; i < count; i += batchSize)  
  18.                 {  
  19.                     tran = cn.BeginTransaction();  
  20.                     cmd.Transaction = tran;  
  21.                     for (int j = i; j < i + batchSize && j < count; j++)  
  22.                     {  
  23.                         sql.AppendFormat("Insert into TestTable(Id, Name) Values({0}, '{1}');", dt.Rows[j]["Id"], dt.Rows[j]["Name"]);  
  24.                     }  
  25.                     //LogHelper.Info(sql.ToString());  
  26.                     cmd.CommandText = sql.ToString();  
  27.                     cmd.ExecuteNonQuery();  
  28.                     tran.Commit();  
  29.                     sql.Clear();  
  30.                 }  
  31.                 flag = true;  
  32.   
  33.             }  
  34.             catch (Exception ex)  
  35.             {  
  36.                 try  
  37.                 {  
  38.                     tran.Rollback();  
  39.                 }  
  40.                 catch (Exception tex)  
  41.                 {  
  42.                     LogHelper.Error(sql.ToString(), tex);  
  43.                 }  
  44.                 LogHelper.Error(sql.ToString(), ex);  
  45.                 return false;  
  46.             }  
  47.             finally  
  48.             {  
  49.                 if (tran != null) tran.Dispose();  
  50.                 if (cn != null)  
  51.                 {  
  52.                     if (cn.State == ConnectionState.Open)  
  53.                     {  
  54.                         cn.Close();  
  55.                     }  
  56.                     cn.Dispose();  
  57.                 }  
  58.                 if (cmd != null) cmd.Dispose();  
  59.             }  
  60.             return flag;  
  61.         }  
  62.         #endregion  

 

结果如下:

Use SqlServer Batch SqlTransaction Insert;RecordCount:40000;BatchSize:10;Time:8647;

Use SqlServer Batch SqlTransaction Insert;RecordCount:40000;BatchSize:20;Time:6255;

Use SqlServer Batch SqlTransaction Insert;RecordCount:40000;BatchSize:50;Time:5093;

Use SqlServer Batch SqlTransaction Insert;RecordCount:40000;BatchSize:100;Time:4529;

Use SqlServer Batch SqlTransaction Insert;RecordCount:40000;BatchSize:200;Time:4469;

 

没有测试更多的条数,但可以看出来使用事务以后插入速度还是明显提高了(忽略拼接sql语句的耗时),为什么?

 

我们知道,就SQL Server而言,使用mdf文件保存表、索引等内容,ndf文件忽略,ldf文件保存数据库日志,存储了数据文件的变更信息。

 

在默认情况下,SQL Server每条插入语句都是一个事务,在这个插入操作中,SQL Server会先修改内存中的数据页标志其为已修改,然后是写数据库日志文件,后台会有单独的线程周期性地检查并将修改写入到mdf文件中。

 

写日志文件的操作总是顺序的,而写数据文件则通常是随机的,每次插入一条数据,硬盘的磁头都会频繁移动,而且写日志的操作和延迟的写线程同时进行时,此问题就更严重了,如果能将数据文件和日志文件存放在不同的磁盘上,性能应该会有更好的提升。

 

使用事务后,在一个事务中,将多次对日志文件的修改变成一次修改,所以性能反而得到了提升。

 

但虽然事务可以改善硬盘的吞吐量,但它也会阻塞其他线程,所以需要进行测试已找到合适的平衡点。

 

另两种事务的使用方式中使用ADO.NET的SqlTransaction类显然更为让开发人员接受,除非你是打算在拼接sql语句中加入一些特殊的操作。

0 0
原创粉丝点击