优化大容量导入指南
本主题提供优化多种大容量导入方案的性能的指南:
将数据从单个客户端(或数据流)导入到空表中。
将数据从单个客户端(或数据流)导入到已部分填充的非空表中。
注意 将数据导入到非空表中也称为“增量大容量导入”。增量大容量导入的关键问题在于是否应该预先删除索引。
在表级锁定的同时从多个客户端(或数据流)导入数据。
在 Microsoft SQL Server 实例之间复制数据。
本主题还概述了大容量导入操作中的表锁定和日志记录。
将数据从单个客户端(或数据流)导入到空表中
将数据从单个客户端(或数据流)导入到空表中时,Microsoft 建议执行下列操作:
指定 TABLOCK 限定符(在所有三种大容量导入方法中都可以使用,可以是提示或选项)。使用 TABLOCK 会导致在大容量操作期间进行表级锁定,并消除按行锁定的开销。有关详细信息,请参阅控制大容量导入的锁定行为。
最小化日志。有关详细信息,请参阅在大容量导入中按最小方式记录日志的前提条件。
对索引进行如下处理。
使用 bcp、BULK INSERT 或 INSERT ...SELECT * FROM OPENROWSET(BULK...) 时,如果表为空但有聚集索引,并且数据文件中的数据进行了排序以与聚集索引键列匹配,则还应执行下列操作:
大容量导入已带有聚集索引的数据。
指定 ORDER 提示以及 TABLOCK 提示。有关详细信息,请参阅控制大容量导入数据时的排序顺序。
对于空表,此方法明显快于导入数据后再创建聚集索引,因为它不需要执行排序。
注意 如果非空表带有索引,则大容量导入将完整地记入日志,即使在大容量日志恢复模式下也是如此。若要确定是否删除索引,请考虑对于大容量导入到没有索引的表来说,所带来的好处是否大于删除和重新创建索引的成本。
如果将数据大容量导入到带有索引的空表中并指定批的大小,则在导入第一批数据后表将变成非空表。从第二批开始,数据将完整地记入日志。对于带有索引的空表,请考虑用一批完成整个大容量导入。
注意 如果未指定批大小,则默认情况下,SQL Server 查询优化器假设默认大小为数据文件的大小。为了改善性能,可以使用 ROWS_PER_BATCH 或 KILOBYTES_PER_BATCH 限定符向优化器提示数据文件中的大约行数。有关详细信息,请参阅管理大容量复制的批大小。
通常,大容量导入到不带索引的表中要快于大容量导入到带索引的表中。因此,如果空表带有索引,则应该在导入数据前删除索引,等导入后再重新创建。如果数据未按照聚集键列进行排序,或者表为空表,请删除所有索引,导入数据,然后再创建新的索引。
将数据从单个客户端(或数据流)导入到非空表中
将数据导入到已含有数据的表(非空表)中称为“增量大容量导入”。增量大容量导入的关键问题在于是否应该预先删除索引。您有两种选择:可以保留索引,也可以删除索引,之后再重新创建。
将数据从单个客户端(或数据流)导入到非空表中时,是否保留索引取决于导入的新数据的量与表中现有数据量之比:
如果导入的新数据量与现有数据量相比较小,则删除并重新生成索引的效率可能很低。重新生成索引所需的时间很可能比大容量操作节省的时间更长。
相反,如果导入的新数据量相对较大,则在执行大容量操作前删除表中的索引可以提高性能,而且不会明显增加建立索引所需的时间。
下表列出了删除索引前表中应包含的新数据的最小数据量。最小数据量按照表中总数据量的百分比的方式计算。根据索引的类型和组合的不同,最小数据量也会不同。如果新数据超过了给定类型的索引或索引分组的建议百分比,请考虑在进行大容量操作之前删除索引,之后再重新创建。这些数字与现有数据及要加载的数据的数据样式相关。因此,提供这些数字仅作为一般标准。
索引 |
新数据的相对数据量 |
---|---|
只有聚集索引 |
30% |
有聚集索引和一个非聚集索引 |
25% |
有聚集索引和两个非聚集索引 |
25% |
只有一个非聚集索引 |
100% |
有两个非聚集索引 |
60% |
在表级锁定的同时从多个客户端(或数据流)导入数据
如果 SQL Server 运行于具有多个处理器的计算机上,并且要大容量导入到表中的数据可以分为多个数据文件,则可以通过将数据从多个客户端并行导入到表中来提高性能。从多个客户端大容量导入到一个表中时,每个客户端必须具有各自的输入数据文件。
在将数据从多个客户端导入到一个表中时,应考虑下列因素:
多个大容量导入流可能会互相阻塞。
为了防止这种情况,SQL Server 提供了一个专用的内部锁,称为大容量更新 (BU) 锁。若要使用 BU 锁,需要为每个大容量导入流指定 TABLOCK 选项,这样就不会阻塞其他大容量导入流。从而避免了客户端之间的表访问冲突。但是,BU 锁只能在没有索引的表(无论是空表还是非空表)中使用。如果针对具有索引的表指定了 TABLOCK,则无法执行并行大容量导入。有关详细信息,请参阅控制大容量导入的锁定行为。
如果表中带有索引,则可以在大容量导入数据之前删除所有的索引,以便利用 BU 锁定。然后使用 TABLOCK 并行地大容量导入数据,之后再重新创建索引。另外注意,如果非空表带有索引,则大容量导入将完整地记入日志,即使在大容量日志恢复模式下也是如此。若要确定是否删除索引,请考虑对于大容量导入到没有索引的表来说,所带来的好处是否大于删除和重新创建索引的成本。
注意 如果删除了辅助索引,可考虑通过从单独的客户端创建每个辅助索引的方法,并行地重新创建这些索引。
若要避免删除并重新创建索引,可以执行并行导入,而不指定 TABLOCK 提示。但在这种情况下,多个大容量导入数据流可能会互相阻塞,而且不能使用大容量日志优化。为了尽可能减少阻塞,可以指定较小的批大小,并使用 ORDER 提示去掉大容量导入操作中的排序步骤。
必须将数据分为多个输入文件,每个文件使用一个客户端。若要最高效地使用 CPU,应使数据文件大小接近。
有关详细信息,请参阅使用表级锁定并行导入数据。
大容量导入过程中的表锁定和日志记录
下表概述了在大容量导入操作中,如何通过表架构确定锁类型。下表还指示表是否为空,是否为操作设置 TABLOCK,以及数据库使用大容量日志恢复模式时出现的日志记录类型。
注意 |
---|
在第一个成功的批处理之后,表不再为空。 |
大容量导入目标表 |
表是否为空? |
是否设置 TABLOCK? |
锁数 |
大容量日志恢复模式和简单恢复模式下的日志记录 |
---|---|---|---|---|
堆 |
是 |
是 |
BU-Tab |
大容量日志记录 |
堆 |
是 |
否 |
IX-Tab |
完整日志记录 |
堆 |
否 |
是 |
BU-tab |
大容量日志记录 |
堆 |
否 |
否 |
IX-Tab |
完整日志记录 |
带有一个非聚集索引的堆 |
是 |
是 |
SCH-M |
大容量日志记录 |
带有一个非聚集索引的堆 |
是 |
否 |
IX-Tab |
完整日志记录 |
带有一个非聚集索引的堆 |
否 |
是 |
SCH-M |
|
带有一个非聚集索引的堆 |
否 |
否 |
IX-Tab |
完整日志记录 |
聚集索引 |
是 |
是 |
SCH-M |
大容量日志记录 |
聚集索引 |
是 |
否 |
IX-Tab |
完整日志记录 |
聚集索引 |
否 |
是 |
X-TAB |
完整日志记录 |
聚集索引 |
否 |
否 |
IX-Tab |
完整日志记录 |
在 SQL Server 实例之间复制数据
若要将数据从 SQL Server 的一个实例大容量复制到另一个实例,请使用 bcp 将表数据导出到数据文件中。然后使用大容量导入方法之一将数据从文件导入到表中。在执行大容量导出或大容量导入操作时,可以使用本机格式或 Unicode 本机格式。
注意 |
---|
有关这些格式的详细信息,请参阅使用本机格式导入或导出数据和使用 Unicode 本机格式导入或导出数据。 |
如果源表带有聚集索引或者打算将数据大容量导入到带聚集索引的表中:
通过使用带有 query 选项(该选项指向一个 SELECT 语句)的 bcp 语句将源表中的数据大容量导出,同时使用适当的 ORDER BY 子句创建有序的数据文件。有关详细信息,请参阅 bcp 实用工具。
在将数据大容量导入到 SQL Server 中时,使用 ORDER 限定符。只有 bcp 和 BULK INSERT 支持该限定符。有关详细信息,请参阅控制大容量导入数据时的排序顺序。
有关详细信息,请参阅在服务器间复制数据。