Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#数据库中的多个并行插入_C#_Multithreading_Ado.net_Sqlcommand - Fatal编程技术网

C#数据库中的多个并行插入

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

我有一个大约3000行的数据表。这些行中的每一行都需要插入到数据库表中。目前,我正在运行foreach循环,如下所示:

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以后的版本)。