C#数据库中的多个并行插入
我有一个大约3000行的数据表。这些行中的每一行都需要插入到数据库表中。目前,我正在运行foreach循环,如下所示:C#数据库中的多个并行插入,c#,multithreading,ado.net,sqlcommand,C#,Multithreading,Ado.net,Sqlcommand,我有一个大约3000行的数据表。这些行中的每一行都需要插入到数据库表中。目前,我正在运行foreach循环,如下所示: obj_AseCommand.CommandText = sql_proc; obj_AseCommand.CommandType = CommandType.StoredProcedure; obj_AseCommand.Connection = db_Conn; obj_AseCommand.Connection.Open(); foreach (DataRow dr i
obj_AseCommand.CommandText = sql_proc;
obj_AseCommand.CommandType = CommandType.StoredProcedure;
obj_AseCommand.Connection = db_Conn;
obj_AseCommand.Connection.Open();
foreach (DataRow dr in dt.Rows)
{
obj_AseCommand.Parameters.AddWithValue("@a", dr["a"]);
obj_AseCommand.Parameters.AddWithValue("@b", dr["b"]);
obj_AseCommand.Parameters.AddWithValue("@c", dr["c"]);
obj_AseCommand.ExecuteNonQuery();
obj_AseCommand.Parameters.Clear();
}
obj_AseCommand.Connection.Close();
您能告诉我如何在数据库中并行执行SP吗?因为上述方法大约需要10分钟来插入3000行。Edit
事后看来,使用Parallel.ForEach
来并行化DB插入稍微有些浪费,因为它还会为每个连接消耗一个线程。可以说,一个更好的并行解决方案是使用异步版本的System.Data
Db操作,例如,启动执行(并发),然后使用wait Task.WhenAll()
等待完成-这将避免调用方的线程开销,尽管总体Db性能可能不会更快
原始答案,多个并行插入数据库
您可以使用TPL并行执行此操作,例如专门使用的localInit
重载。您几乎肯定希望通过调整来限制并行量,这样您就不会淹没数据库:
Parallel.ForEach(dt.Rows,
// Adjust this for optimum throughput vs minimal impact to your other DB users
new ParallelOptions { MaxDegreeOfParallelism = 4 },
() =>
{
var con = new SqlConnection();
var cmd = con.CreateCommand();
cmd.CommandText = sql_proc;
cmd.CommandType = CommandType.StoredProcedure;
con.Open();
cmd.Parameters.Add(new SqlParameter("@a", SqlDbType.Int));
// NB : Size sensitive parameters must have size
cmd.Parameters.Add(new SqlParameter("@b", SqlDbType.VarChar, 100));
cmd.Parameters.Add(new SqlParameter("@c", SqlDbType.Bit));
// Prepare won't help with SPROCs but can improve plan caching for adhoc sql
// cmd.Prepare();
return new {Conn = con, Cmd = cmd};
},
(dr, pls, localInit) =>
{
localInit.Cmd.Parameters["@a"] = dr["a"];
localInit.Cmd.Parameters["@b"] = dr["b"];
localInit.Cmd.Parameters["@c"] = dr["c"];
localInit.Cmd.ExecuteNonQuery();
return localInit;
},
(localInit) =>
{
localInit.Cmd.Dispose();
localInit.Conn.Dispose();
});
注意事项:
- 除非你真的知道你在做什么,一般来说,我们应该让TPL来决定并行度。但是,根据资源的争用程度(读取:数据库工作的锁),可能需要限制并发任务的上限(尝试和错误可能很有用,例如,尝试并发4、8、16个并发任务等,以查看哪个并发任务的吞吐量最大,并监视Sql Server上的锁定和CPU负载
- 类似地,离开TPL的默认分区器通常足以在任务之间对数据行进行分区
- 每个任务都需要自己独立的Sql连接
- 与其在每次调用时创建和处理命令,不如在每个任务中创建一次命令,然后继续重用相同的命令,每次只更新参数
- 使用LocalInit/Local lambdas进行每个任务的设置和清理,如处理命令和连接 如果您使用的是AdHoc SQL或,您也可以考虑使用。
- 我假设枚举
行是线程安全的。当然,您需要仔细检查这一点数据表的
即使有一个宽表和一个线程,3000行的10分钟也是多余的。你的进程做什么呢?我假设处理过程并不琐碎,因此需要存储过程,但是如果你只是做简单的插入,按照@3dd的评论,在一个相当窄的表上每分钟会产生约1M行的插入。最好通过w将数据表插入数据库
obj_AseCommand.CommandText = sql_proc;
obj_AseCommand.CommandType = CommandType.StoredProcedure;
obj_AseCommand.Connection = db_Conn;
obj_AseCommand.Connection.Open();
obj_AseCommand.Parameters.AddWithValue("@Parametername",DataTable);
obj_AseCommand.ExecuteNonQuery();
在数据库中,您必须创建与数据表完全匹配的表类型
CREATE TYPE EmpType AS TABLE
(
ID INT, Name VARCHAR(3000), Address VARCHAR(8000), Operation SMALLINT //your columns
)
在存储过程中,您可以这样做
create PROCEDURE demo
@Details EmpType READONLY // it must be read only
AS
BEGIN
insert into yourtable //insert data
select * from @Details
END
您可以使用SqlBulkCopy
指南是您可以使用
SqlBulkCopy
。请参阅下面的示例代码。WriteToServer
方法将datatable
写入数据库,前提是它们具有相同的映射
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(ConSQL)) {
if (ConSQL.State == ConnectionState.Closed) {
ConSQL.Open();
}
bulkCopy.ColumnMappings.Add(0, 0);
bulkCopy.ColumnMappings.Add(1, 1);
bulkCopy.ColumnMappings.Add(2, 2);
bulkCopy.DestinationTableName = "dbo.TableName";
bulkCopy.WriteToServer(dataTable);
bulkCopy.Close(); //redundant - since using will dispose the object
}
出于好奇-为什么要用存储过程向数据库中添加3000行?如果这是来自某个输入文件,为什么不使用某种管理工具将其直接导入数据库中?datatable是从其他数据库(主数据库)填充的。我的目标是从主数据库中获取数据并将其插入数据库中。T存储过程用于在我的数据库中插入数据。为什么不使用bulk insert或链接服务器,让SQL获取并插入数据呢?我认为使用bulk insert/import/无论您的DBMS如何称呼它都更好。这将为您节省大量的麻烦和时间。这更多的是一个评论,而不是回答。对不起,我很抱歉一个新用户:)这是一个有效的答案(虽然可能不是一个好的答案),可以通过添加一些解释来改进。嗨,斯图尔特,谢谢你的输入。我通过使用AseBulkCopy使用下面的代码进行了尝试,因为我们有Sybase数据库AseBulkCopy obj_AseBulkCopy=new AseBulkCopy(db_Conn);obj_AseBulkCopy.DestinationTableName=“db_table”;obj_AseBulkCopy.BatchSize=1000;数据库连接打开();obj_AseBulkCopy.WriteToServer(dt);db_连接关闭();然而,它仍然需要相同的时间来执行。我在这段代码中遗漏了什么吗?如果一个大容量拷贝需要10分钟才能完成3000行,那么您的RDBMS就会发生一些非常奇怪的事情。我猜您在插入的表上有触发器,这些触发器执行很多逻辑,或者有很多锁争用,或者可能有很多约束、规则、索引,并且表非常宽。您需要一位DBA来认真研究表插入技术和缺乏并行性并不是这里的瓶颈:(这可能是SQL Server 2008以后的版本)。