【问题标题】:How to fix slow running SSIS package如何修复运行缓慢的 SSIS 包
【发布时间】:2019-04-24 07:30:08
【问题描述】:

我正在尝试将大量数据从 PROD DB 中的一个表插入到存档 DB 中的表中。表具有相同的架构,存档表具有下降的索引和“身份插入”。我只需要插入 Arh DB/Table 中不存在的记录。 我在 SSIS 序列包中使用“执行 SQL 任务”,它的工作速度非常慢(使用 20000 插入批量大小)。我在 10 分钟内插入了 20000 条记录。顺便提一下,我需要插入 48000000 条记录。 SQL Server 是 2016 标准版。 有什么解决办法吗?

SQL 查询是:

SELECT TOP (@InsertBatchSize) s.ID,.....and other columns 
FROM PRODDB.dbo.source_table AS s WITH (NOLOCK) 
    INNER JOIN ArchiveDB.dbo.MissingIDsTable AS t WITH (NOLOCK) 
    ON s.ID = t.ID 
WHERE s.ID not in (SELECT ID 
                   from ArchiveDB..destination_table 
                   WHERE IsUpdated is null ) 

【问题讨论】:

  • 20K 行应该需要几秒钟。不要使用“执行 SQL”来移动数据。这与简单地执行 SQL 查询或存储过程相同。移动数据是数据流的工作。至于仅移动更改的行,您如何首先检测到行更改?左连接?使用存储的 ID 值?使用更改跟踪?
  • 如果您使用“执行 SQL”,延迟是由查询引起的,而不是 SSIS。查询有什么作用?它从哪里获取数据?
  • 首先我将丢失的 ID 插入到 ARH_DB 端的一个物理表中。这工作得很快。此外,我在名为 isupdated 的 Arh_table 中有一个标志,当我更新已插入的列时,我正在用值 1 更新这个标志(这一步没问题)。目标(存档表)在插入名为 IsInserted 的新记录时默认包含 1。
  • ----Bellow 是选择应该移动的数据的代码-------- SELECT TOP (@InsertBatchSize) s.ID,.....等列FROM PRODDB.dbo.source_table AS s WITH (NOLOCK) INNER JOIN ArchiveDB.dbo.MissingIDsTable AS t WITH (NOLOCK) ON s.ID = t.ID WHERE s.ID not in (SELECT ID from ArchiveDB..destination_table WHERE IsUpdated 是空)
  • 在问题本身中发布代码。虽然很明显您已经发现查询速度很慢,并试图以错误的方式解决这个问题。 NOLOCK 不是“不拿锁”的意思,而是“不尊重别人的锁,读脏数据,自己拿多余的锁”

标签: sql sql-server ssis


【解决方案1】:

Execute SQL 不适合数据传输。它不能批处理数据,也不能转换它们。这就是 Dataflow 任务的工作。 Dataflow 任务允许使用 firehose 游标读取源数据,并使用最少的日志记录使用批处理批量操作将它们写入数据目标。它的速度取决于源查询。慢查询会导致执行慢。

这个问题缺少很多信息,比如源数据库和目标数据库中的表模式。从"Identity insert on" 我怀疑这些表有一个 ID 列,它是源中的一个 IDENTITY 和主键。如果你只关心新记录,你可以编写一个只读取自上次执行以来的数据的源查询,例如:

SELECT s.ID,.....and other columns 
FROM PRODDB.dbo.source_table AS s
where ID>@maxId

其中@maxId 是提供给源查询的查询参数。不需要批处理,SSIS 可以根据数据源、批处理大小的目标设置等来完成。

这个查询应该只用于加载数据。要制作初始副本,请使用不同的数据流,其源查询不会过滤任何内容。使用 Where ID>-1 之类的东西会返回所有数据,但只有在扫描整个索引之后才会返回。当我们已经要复制所有数据时,为什么要这样做

ID 也应该是 target 表中的主键。这将加速加载参数值的select MAX(ID) from target 操作。它还将检测和防止不可避免的重复错误。无论我们多么小心,其他人总是会犯错误,从而导致重复数据。

您可以通过在插入前禁用索引并在导入操作后重新启用它来提高导入性能。

这只是检测更改和复制数据的一种方式。另一种技术是在源表中启用Change Tracking 并检索自上次作业运行以来修改的行。

您还可以将修改后的数据复制到临时表中,并将 与目标连接到 INSERT/UPDATE 更改。 ID 或更改跟踪可用于查找修改的数据。这样做的好处是可以快速释放源代码。

【讨论】:

    【解决方案2】:

    任务 - 数据库之间的数据传输,目标数据库位于 SQL 2016 服务器上。
    我会推荐以下方法:

    1. 在 ArchDB 表中创建用于分段加载。最好在单独的模式中创建它并使用与原始表相同的名称,只是为了管理和可读性。
    2. 使用以下步骤创建 SSIS 包:

      • 在 Exec SQL 任务中清理必要的临时表
      • 数据流任务 - 将所有数据从源移动到 ArchDB 中的临时表。在数据流上——基于 ArchDB 目标表定义一个全缓存的 Lookup 组件;该组件将检查 ArchDB 中是否存在记录。只拾取错过的记录。
      • 将数据从 Arch DB 中的暂存表移动到目标表,如

      INSERT INTO WITH (TABLOCK) SELECT ... FROM

    评论:

    • DFT 中的查找组件用于过滤掉现有记录。由于您在 ArchDB 目标表上没有任何索引,因此您必须使用完全缓存,但会占用大量内存。
    • INSERT WITH (TABLOCK) constriction 用于利用 SQL 2016 的并行插入功能。
    • 您可以不使用临时表并直接插入到目标表中。但是,调试会更加困难。

    【讨论】:

    • 48M 数据的查找任务?这基本上就是 OP 已经对 MissingIDsTable 表所做的事情。事实上 MissingIDsTable 更好,因为它可能只包含实际的不匹配。
    • @PanagiotisKanavos,为什么不呢?如果只使用 ID 进行查找,1 行 bigint 大约为 16 字节,48M * 16 字节 ~ 0.8 GB。作者在 ArchiveDB 上没有索引。此缓存将与查询的NOT IN 部分执行相同的操作。
    • 因为在计算 MissingIDsTable 时该工作已经完成。 800MB 是 很多 不再可用于缓冲行的 RAM。如果 SSIS 在服务器上运行,则有 800MB 不再可供 SQL Server 使用。最后,这种查找与 SQL Server 本身对 JOIN 所做的几乎没有什么不同,例如对 AllTargetIDs 表的查找。不过,服务器将能够以更好的方式管理内存
    • 如果您知道 ID 字段正在递增,并且不希望更新和删除,那么您知道源中的任何更改都可以只有一个大于目标的 MAX(ID) 的 ID。
    • @PanagiotisKanavos,根据查询,MissingIDsTable 是某种过滤器;然后检查过滤后的数据是否存在于destination_table 中。如果没有索引 - 准备好进行表扫描和排序,以便在 SQL 端使用磁盘/TempDB 操作进行合并/散列连接。如果您有 RAM 并且需要考虑性能 - SSIS 可以做一些事情。
    猜你喜欢
    • 1970-01-01
    • 2019-05-26
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多