SSIS - 插入大量行(数亿行)的最佳方法
Posted
技术标签:
【中文标题】SSIS - 插入大量行(数亿行)的最佳方法【英文标题】:SSIS - Best way to insert large number of rows (hundred millions of rows) 【发布时间】:2015-10-16 00:27:54 【问题描述】:下面是这样的场景:我有一个 500GB 大小的 XML 文件,其中包含大约 6 亿行的数据(一次在数据库表上)。我正在使用 SSIS 进行操作,因为如果我要使用 SSIS 组件(即:XML Source
)它会消耗大量内存,它可能会导致超时(如果我错了,请纠正我,但是据我所知,使用 SSIS 上的组件会将 XML 的内容加载到内存中 - 文件那么大肯定会导致错误)。我的做法是:
Script Task
使用 XML Reader 解析 XML 数据(到目前为止,XML Reader 是最好的方法,因为它以转发、非缓存的方法解析 XML)
在DataTable
上插入数据
DataTable
上的每500,000行,使用SqlBulkCopy
将内容插入数据库,然后清除DataTable
的内容
我的问题是,目前,我尝试用它解析另一个大小为 200GB 的文件,它的运行速度约为 13.5M / 1 小时 - 我不知道它是否仍然适用于该运行时间。它确实解决了我的问题 - 但它并不太优雅,我的意思是,应该有其他方法。
我正在寻找其他方法,例如:
将大型 XML 文件分成小块 CSV(大约 20GB),然后使用 SSISData Flow task
每个新行使用INSERT
脚本
你能帮我决定哪个最好吗?或提出任何其他解决方案。
每一个答案都将不胜感激。
编辑
我忘了提 - 我的方法是动态的。我的意思是,有许多表将填充大型 XML 文件。因此,使用脚本组件作为源可能不是那么有用,因为我仍然需要定义输出列。不过还是要试试看。
编辑 2015-07-28
该文件来自我们的客户,对于他们想要发送给我们的来源,我们无能为力。 XML,就是这样。这是我正在使用的 XML 示例:
<?xml version="1.0" encoding="UTF-8"?>
<MFADISDCP>
<ROW>
<INVESTMENT_CODE>DATA</INVESTMENT_CODE>
<DATE_OF_RECORD>DATA</DATE_OF_RECORD>
<CAPITAL_GAIN_DISTR_RATE>DATA</CAPITAL_GAIN_DISTR_RATE>
<INCOME_DISTR_RATE>DATA</INCOME_DISTR_RATE>
<DISTR_PAYMENT_DATE>DATA</DISTR_PAYMENT_DATE>
<CURRENCY>DATA</CURRENCY>
<CONFIRM>DATA</CONFIRM>
<EXPECTED_DISTRIBUTION_AMOUNT>DATA</EXPECTED_DISTRIBUTION_AMOUNT>
<KEYING_STATUS>DATA</KEYING_STATUS>
<DAF_RATE>DATA</DAF_RATE>
<INCOME_START_DATE>DATA</INCOME_START_DATE>
<ALLOCABLE_END_DATE>DATA</ALLOCABLE_END_DATE>
<TRADE_DATE>DATA</TRADE_DATE>
<OVR_CAPITAL_GAIN_DISTR_OPTION>DATA</OVR_CAPITAL_GAIN_DISTR_OPTION>
<OVR_INCOME_DISTR_OPTION>DATA</OVR_INCOME_DISTR_OPTION>
<BACKDATED_DISTRIBUTION>DATA</BACKDATED_DISTRIBUTION>
<DATE_MODIFIED>DATA</DATE_MODIFIED>
</ROW>
<!--AROUND 49M+ OF THIS ROWS-->
</MFADISDCP>
【问题讨论】:
您可以有一个带有脚本组件的单个数据流任务,该脚本组件只是将数据直接流式传输到批量大小为 500,000 的批量加载目标。 @MartinSmith - 请查看我对这个问题的更新。 解决瓶颈问题。它很可能是您的 XML 粉碎机,但也可能是……网络、内存、磁盘、数据库中的争用(您有排他锁吗?)。源必须是 XML 吗?例如,我建议您将其预先分解为 CSV,然后将其上传,您可能会发现 XML>CSV 比 CSV>Database 花费的时间要长得多 在考虑将数据存储在文件中时,请考虑存储结构的开销。对于您拥有的每个元素,最精简的 XML 将花费 7 字节的存储空间。几百行,地狱,几十万行,那罪孽并不可怕。十亿行虽然是站不住脚的。根据存储的内容,您可能会在结构上花费四分之一到一半的存储成本。也许您不关心存储成本,但还有读取访问成本。如果可以将我的 IOPS 从 500GB 降低到 375GB,我读取 EOF 的时间从 ~3 小时到 2.2 @ 50MB/s 您可以在这里看到许多不将源存储为 XML 的原因。目前尚不清楚是否可以更改它。你能澄清一下吗?特别是当您说该文件有 52 列和 49M+ 行时……那是表格数据。以 XML(一种详细的树格式)存储表格数据是没有意义的。如果您必须以花哨的格式存储,JSON 是一种树结构并且需要较少的字符来存储。我再次建议您对 XML 粉碎机(脚本任务,但不提供任何内容)进行计时,并查看该部分需要多长时间。我再次问:为什么需要 XML?您是否以分层格式获取数据? 【参考方案1】:如果我要这样做,那么我会将其分解为以下任务:
-
将 XML 文件转换为(制表符或逗号)分隔文件。如果您的服务器有快速磁盘 (SSD),那么这应该非常快。请注意数据中可能包含可能破坏分隔符格式的特殊字符的字符串。不要使用
DataTable
对象,因为它很慢。您可以将其流式传输,这样您就不需要一次性将整个文件保存在内存中(除非您的服务器有数百 GB 的内存)
截断数据库中用于加载数据的阶段表。
使用 SQL Server 的bcp.exe
将分隔文件推送到数据库的暂存表中。这可能是将大量数据放入数据库的最快方法。这样做的一个问题是,如果它失败了,那么很难找到是哪一行数据导致了失败。
删除分隔文件,因为您不需要它们占用大量空间。
创建一个 SQL 存储过程,将数据从暂存表移动到您将使用它的任何位置。
您可以为此使用 SSIS 脚本任务,也可以编写自己的独立服务。
注意,这都是理论上的,可能有更好的方法来做这件事,但这可能是一个很好的起点,可以找出你的瓶颈在哪里。
【讨论】:
以上是关于SSIS - 插入大量行(数亿行)的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
使用 Datatables 在 Laravel 中处理大量行的最佳方法是啥?