在单个事务中针对不同表的多个并行(异步)SqlBulkCopy 插入的性能

Performance of Multiple Parallel (async) SqlBulkCopy inserts, against different tables, in a single Transaction

TL;DR

为什么 运行ning 多个 SqlBulkCopy 插入,针对不相关的 tables,async & 并行地,在单个事务上似乎表现得好像它是运行宁串联?


上下文

我有一些代码正在计算和存储大量数据。 计算是预先完成的,所以代码的存储部分得到了要存储的一大堆数据。

我的数据库写入是使用 SqlBulkCopy.WriteToServerAsync 完成的,总的来说,它做得很好。

在我需要存储的东西中,有 6 个 table 与业务相关,但与 SQL 无关。因此,我对他们的写入需要在一个事务中,这样任何一个写入的错误都会恢复对所有其他写入的写入。

此代码的性能相当关键,因此我希望能够并行 运行 BulkInserts。没有 FKey 或任何其他 table 正在与之交互(数据完整性由代码管理)所以我看不出有任何理由认为这不可能。


我目前写的

我以为我知道如何编写所有代码并且能够使它们全部工作,但是有一个我不明白的奇怪的性能下降:

很高兴提供您想要的实际代码,但这已经是一个很长的 Q,而且代码会很长到 0。 LMK 如果你想看什么。

我会写:

在所有情况下,最终的 BulkInsert 看起来像:

using (var sqlBulk = BuildSqlBulkCopy(tableName, columnNames, transactionToUse))
{
    await sqlBulk.WriteToServerAsync(dataTable);
}

private SqlBulkCopy BuildSqlBulkCopy(string tableName, string[] columnNames, SqlTransaction transaction)
{
    var bulkCopy = new SqlBulkCopy(transaction.Connection, SqlBulkCopyOptions.Default, transaction)
    {
        BatchSize = 10000,
        DestinationTableName = tableName,
        BulkCopyTimeout = 3600
    };

    foreach (var columnName in columnNames)
    {
        // Relies on setting up the data table with column names matching the database columns.
        bulkCopy.ColumnMappings.Add(columnName, columnName);
    }

    return bulkCopy;
}


当前性能统计数据

如上所述

前 3 个结果对我来说都很有意义。

#1 vs #2:只要插入都能正常工作,事务就不会做太多事情。数据库仍在相同的时间点做所有相同的工作。

#2 vs #3:这是 运行 并行插入的全部要点。通过 运行 并行插入,我们花更少的时间等待 SQL 完成这件事。我们让 DB 并行执行大量工作,因此虽然没有 6 倍的加速那么多,但也足够了。


问题:

为什么最后一个案例这么慢?我可以修复它吗?

这几乎与串行执行一样慢,并且比并行执行慢 25%,但是有多个事务!

这是怎么回事? 为什么 运行ning 多个 SqlBulkCopy 插入,而不是不相关的 tables,async & 在单个事务上并行插入似乎表现得好像它是 运行ning 串联?


非受骗者:

SqlBulkCopy Multiple Tables Insert under single Transaction OR Bulk Insert Operation between Entity Framework and Classic Ado.net(运行不是并行查询)

(表是相关的,他们正试图从中读回)

Parallel Bulk Inserting with SqlBulkCopy and Azure(这是在谈论并行加载到 单个 table)

在同一 SQL 服务器 connection/transaction 上同时执行多个命令的唯一方法是使用 Multiple Active Result Sets (MARS)。 MARS 用于并行单一事务案例,因为您对每个并行批量复制使用相同的 connection/transaction。

MARS 执行 SELECT 并以交错而非并行方式插入批量操作,因此您将获得与串行执行大致相同的性能。您需要具有不同连接的分布式事务才能在同一事务范围内真正并行执行。