排除 SqlBulkCopy 未执行最少日志记录的问题

Posted

技术标签:

【中文标题】排除 SqlBulkCopy 未执行最少日志记录的问题【英文标题】:Troubleshooting SqlBulkCopy not doing minimal logging 【发布时间】:2013-11-07 04:48:25 【问题描述】:

基于here 提出的想法,我创建了一个QueuedDataReader,它将ConcurrentQueue 包装在IDataReader 中,这样我就可以将它传递给SqlBulkCopy 并将数据“流”到数据库中。

它工作正常,而且速度非常快,但日志文件增长非常快,尽管我相信我已经实现了here、here(以及许多其他地方)建议的所有内容。

两个可能很重要的细节是:

我正在并行插入十几个表(即每个表一个 Task) 这些表有 IDENTITY 列(SqlBulkCopy 没有插入其中),所以我认为“SORT”附带条件不相关。

除此之外:

当我开始插入时,表格是空的。 我已经在 PRIMARY KEY 上尝试了 CLUSTERED 和 NONCLUSTERED 索引,没有太大的明显差异。 我正在使用SqlBulkCopyOptions.TableLock 我尝试过使用和不使用 UseInternalTransactions 以及各种 BatchSizes。 数据库处于简单恢复模式 这些表确实有 FK 约束,但我已尝试禁用/重新启用它们,但它不会停止日志文件的增长(在插入期间)

问题:

我可以查看哪些内容来尝试解决可能导致日志文件增长的问题?

【问题讨论】:

使用 fn_dblog 查看日志内容(网上有说明)。那里的大部分业务是什么?也许您可以发布代表性样本的屏幕截图。你试过没有批量大小(0)吗?因为只有第一批发现表是空的。 @usr,目前正在尝试批量大小为 0,但这也意味着我必须超时 0,这感觉……很危险。我会调查 fn_dblog。 【参考方案1】:

在写我的问题时,我发现了另一个极好的资源 here,它指出 BatchSize 是潜在的罪魁祸首。

结果非常令人惊讶。如果我们使用 BatchSize 参数, 当我们将其设置为较低的值时,性能会变得越来越差。

对网络带宽使用一个非常小的批量值 在整个过程中保持低于 20% 的使用量(请注意 500 批大小图的上限为 25%,这与 其他图表)。任何低于 10.000 的值都会降低性能 非常繁重的方式,导致非常糟糕的时间和大量的日志文件使用。

当我们达到 10.000 的 Batch Size 时,它​​们之间的时间差 各种测试变得非常小。但是,因为我们有 8 个线程 每写入 750.000 行,那么我们只发送了 75 块数据 从每个线程。不用说,获得了最好的性能 当我们使用 0 作为 BatchSize 时,一次发送整个 750.000 行 批处理。

发现是否有任何好处可能会很有趣,除了 从性能,通过使用 BatchSize 参数。我们没有找到 在线书籍中提到的任何东西,我们也从未见过任何东西 在我们的经验中很有趣,这导致我们说最好的 与 BatchSize 相关的事情是将其设为零,这是它的 默认值,因为任何不同于该值的值都会降低 加载过程的性能。

我之前因为超时错误而放弃了BatchSize = 0,但是通过将我的BulkCopyTimeout 设置为零,我得到了一些日志文件增长,但远低于以前。

更新。在搞了太久的手动测试之后,我终于开始着手编写一个自动化测试套件,尝试 BatchSize(0-5000)、排队率和聚集/非聚集索引的变体。 (就我而言,我正在将数据并行写入十几个表)。

这是我发现的:

测试 200000 次插入:如果主键是 CLUSTERED BatchSize 为零,我得到 no 日志文件增长。 100 万次插入的测试:即使 BatchSize=0,日志文件也会增长(尽管比其他 BatchSize 小得多)。 CLUSTERED 仍然阻止日志增长,无论 BatchSize 是多少。 在这些条件之外,一切都取决于插入速度。一旦我“推”得太用力,日志文件就会开始增长。

注意,我的测试以给定的速率将数据写入包装在 IDataReader 中的队列中,该队列由 BulkInsert 出列。每个表都有一个队列/BulkInsert。下一步是尝试使用更大的数据集。

【讨论】:

绝对有可能使用非空表获得最少记录的批量插入。不确定您的情况出了什么问题,因为您发布的“清单”似乎相当不错。我鼓励您查看日志记录以查看其中写入的内容。您应该会发现其中 99% 是分配。 fn_dblog 的输出很难解释(批量插入有很多),但我观察到有很多分配,正如你所说,但相应的释放量.这是预期的吗?它还记录 IDENT_NEWVAL,以及对(非聚集)PK 索引的插入。 好吧,我不知道事情的对错是什么样的,但如果我看到日志内容,我可能会理解它。这是我经常使用的查询:pastebin.com/YZ215dSY。您也可以将其更改为TOP 100 ORDER BY NEWID() 以获得代表性样本。请务必先清除日志。 @usr,目前正在把我的头发扯掉:(很难找到一个足够简化的例子来给出可重复的结果...... 如果您也编写数据库脚本,那么应该会给出可重复的结果。当我运行你的脚本时,我再次将数据量增加了 100 倍,以确保触发“批量行为”并跨越可能存在的任何阈值。

以上是关于排除 SqlBulkCopy 未执行最少日志记录的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何动态开启mysql的慢查询日志记录

C#使用SQLBulkCopy或等效库高效批量删除50000条记录

使用 SQLBulkCopy 插入/更新数据库

是否有可能排除某些服务调用以进行 http 日志记录?

使用 IDataReader 从 SqlBulkCopy 返回记录

对NetBackup 问题进行故障排除的步骤