SQL Server 是否能够对更新语句使用内部并行性?

Edw*_*ian 4 sql-server parallelism update sql-server-2014

我正在努力寻找将一些“varchar”列迁移到“nvarchar”的最佳方法。我使用的选项之一是添加新的 nvarchar 列,然后更新原始列中的值,删除原始列并将新列重命名为旧名称。

我知道它会生成大量的UNDO和REDO数据。不过,我还有其他限制(主要是 SQL Server 不支持并行 DDL 和多列 ALTER 表操作),因此让我们关注如何更快地运行更新语句。

我的 Oracle 经验告诉我使用内部并行性,但是它在 SQL Server 中可用吗?

尽管我特意将该表创建为堆表(无聚集索引),但我无法并行运行此语句。

update t
set new_col_1 = col_1
   ,new_col_2 = col_2
   ...
   , new_col_N = col_N
;
Run Code Online (Sandbox Code Playgroud)

有 3 个文本列可容纳 400GB 数据。AWS RDS 的 IO 性能有限(10000 IOPS)。我们只有 4 小时的停机时间。


在此特定迁移中,不能选择在线重建,因为必须先将数据迁移(到 nvarchar),然后才能启动应用程序。在启动期间,它会检查实际数据类型是否与定义的数据类型(在应用程序元数据存储库中)相对应。

我知道碎片化,但我们别无选择。不过,如果有一些在线重建命令,它可能会很有用,因为我们稍后将能够进行迁移和碎片整理。实际上,作为准备步骤之一,我们正在删除聚集索引。稍后将再次创建该索引,我相信这将解决碎片问题,因为我们将从堆转移到 b*tree 结构。

令人非常沮丧的是我们无法使用任何其他“并行”技术。我正在考虑尝试手动并行更新,通过针对目标表的不重叠范围运行一些并行更新语句。尽管如此,锁升级可能是下一个问题,因为我将在每个更新中更新数百万条记录,并且很可能 SQL Server 将尝试升级到表锁,这将锁定其他更新,并且死机锁定将是最终结果..

Pau*_*ite 10

不,SQL Server 不支持并行更新*。也就是说,尚不清楚并行性是否有助于您的场景中的 I/O 密集型操作。

SQL Server确实支持并行插入行存储堆或聚集列存储(有限制)、选择新堆以及一些并行DDL操作(例如索引构建)。

根据您的 SQL Server 版本、相关表的大小和架构、与其他表的任何关系以及恢复模型,您可能会发现使用并行性和最少日志记录来创建具有更新的列数据类型的新表会更快。即使使用有限的 I/O 云子系统,您也应该测试此选项,而不是假设它会太慢。

如果您使用的是 SQL Server 2016 或更高版本,也可以选择ONLINE更改列。这仅限于一次更改一列,但是是非阻塞的。它还往往会产生组织得更好的最终结果(更少或没有堆转发记录)。

如果您能够跨多个维护时段拆分更改并拥有足够的可用存储空间,则可以使用批处理过程和触发器逐渐从一种模式迁移到另一种模式,以在非维护期间复制更改。最后一步是删除旧表并按照此处所述重命名新表。

您可以使用的一些方法可能会生成大量事务日志(正如您所提到的)。如果您能够升级到 2019 或更高版本,加速数据库恢复将允许主动日志截断(包括正在进行的事务),这可能使这方面不再是问题。

您应该仔细考虑您提出的方法,因为堆仍将包含删除的列,直到完全重建为止,并且堆将具有非常多的转发记录,因为现有行没有足够的空间来容纳额外的列数据。

如果数据频繁更新,请勿使用堆。如果您更新一条记录,并且更新使用的数据页空间多于当前使用的空间,则必须将该记录移动到具有足够可用空间的数据页。这将创建一个指向数据新位置的转发记录,并且必须将转发指针写入先前保存该数据的页中,以指示新的物理位置。这会在堆中引入碎片。扫描堆时,必须遵循这些指针,这会限制预读性能,并且可能会产生额外的 I/O,从而降低扫描性能。


*更新本身永远不会由多个线程执行。更新执行计划中的其他运算符可以使用并行性。当然,SQL Server 还支持从不同连接对同一个表进行多个并发更新,但由于表结构为堆,因此您可能无法确保这些更新可以有效地分离。