插入 37k 行时如何避免 .NET 连接池超时

How to avoid .NET Connection Pool timeouts when inserting 37k rows

我正在尝试找出使用 DAPPER 将大约 37k 行批量插入我的 Sql 服务器的最佳方法。

我的问题是,当我使用 Parallel.ForEach 时 - 与数据库的连接数在短时间内增加 - 最终达到接近或大约 100 ... 这会导致连接池错误。如果我强制最大平行度,那么它会达到最大数量并保持在那里。

设置maxdegree感觉不对。

它目前每秒大约进行 10-20 次插入。这也在一个简单的控制台应用程序中 - 所以除了我的 Parallel.ForEach 循环中发生的事情之外,还有 没有 其他数据库 activity。

在这种情况下使用 Parallel.ForEach 是否是错误的,因为这不是 CPU 绑定?

我应该使用 async/await 吗?如果是这样,是什么阻止了它一次进行数百次数据库调用?

基本上就是我正在做的示例代码。

var items = GetItemsFromSomewhere(); // Returns 37K items.

Parallel.ForEach(items => item)
{
    using (var sqlConnection = new SqlConnection(_connectionString))
    {
        var result = sqlConnection.Execute(myQuery, new { ... } );
    }
}

我对此的(不正确的)理解是任何时候都应该有大约 8 个左右的连接到数据库。连接池会释放连接(在连接池中保持实例化状态,等待使用)。如果 Execute 需要 .. 我不知道 .. 甚至可以说 1 秒(插入的最长 运行 时间约为 500 毫秒 .. 每 100 次左右就有 1 次)...没关系.. 该线程被阻塞并冷却,直到 Execute 完成。然后范围完成(并且 Dispose 被自动调用)并且连接关闭。关闭连接后,Parallel.ForEach 获取集合中的下一项,进入连接池,然后获取一个备用连接(记住 - 我们刚刚关闭了一个连接,一瞬间)... rinse.repeat.

这是错误的吗?

备注:

您应该使用 SqlBulkCopy 而不是一个一个地插入。更快更高效。

https://msdn.microsoft.com/en-us/library/ex21zs8x(v=vs.110).aspx

感谢答案所有者 Sql Bulk Copy/Insert in C#

首先:如果是性能方面的,就用SqlBulkCopy。这适用于 SQL-Server。如果您使用其他数据库服务器,它们可能有自己的 SqlBulkCopy 解决方案(Oracle 有一个)。

SqlBulkCopy 的工作方式类似于批量-select:一个状态打开一个连接并将所有数据从服务器流式传输到客户端。对于插入,它以相反的方式工作:它将所有新记录从客户端流式传输到服务器。

参见:https://msdn.microsoft.com/en-us/library/ex21zs8x(v=vs.110).aspx

如果您坚持使用并行,您可能需要考虑以下代码:

void BulkInsert<T>(object p)
{
    IEnumerator<T> e = (IEnumerator<T>)p;
    using (var sqlConnection = new SqlConnection(_connectionString))
    {
        while(true)
        {
            T item;
            lock(e)
            {
                if (!e.MoveNext())
                    return;
                item = e.Current;
            }
            var result = sqlConnection.Execute(myQuery, new { ... } );
        }
    }
}

现在创建您自己的线程并使用一个相同的参数在这些线程上调用此方法:遍历您的集合的迭代器。每个威胁打开自己的连接一次,开始插入,插入所有项目后,关闭连接。此解决方案使用与您创建的线程一样多的连接。

PS:上述代码的多种变体是可能的。您可以从后台线程、任务等调用它。我希望您明白这一点。