并行创建 SQL Server 聚集索引

Posted

技术标签:

【中文标题】并行创建 SQL Server 聚集索引【英文标题】:SQL Server clustered index creation in parallel 【发布时间】:2013-10-08 00:04:06 【问题描述】:

我有两张桌子T_AT_B

两者都是空的。 两者都有聚集索引。 恢复模型设置为SIMPLE。 insert...select.. 满足最小日志记录的要求。看 http://msdn.microsoft.com/en-us/library/ms191244.aspx 两个临时表都包含大量数据。

我需要从临时表中导入数据。

如果我单独执行以下 T-SQL 块,每个需要 2 到 3 分钟才能完成。总时间约为 5 到 6 分钟。

BEGIN TRAN
  INSERT INTO T_A WITH(TABLOCK) FROM SRC_A WITH(NOLOCK);
COMMIT TRAN

BEGIN TRAN
  INSERT INTO T_B WITH(TABLOCK) FROM SRC_B WITH(NOLOCK);
COMMIT TRAN

为了加快速度,我在 SMSS 中打开两个会话并并行执行这两个块。令我惊讶的是,每节课大约需要 10 到 12 分钟才能完成。加起来总时间增加了一倍多。显示的wait_typePAGEIOLATCH_SH,它指向磁盘I/O 瓶颈。我不明白的是,即使两个会话必须相互等待 I/O,它也不应该等待那么久。谁能帮忙解释一下?

我的故事还没有结束。然后我删除了两个表上的聚集索引,并在不同的会话中并行运行了两个块。这一次每次大约需要 1 分钟才能完成。由于它们是并行的,因此总时间约为 1 分钟。伟大的!但是当我尝试重新创建聚集索引时,噩梦就来了。

如果我单独创建集群索引,则每个需要 4 分钟才能完成。总时间约为8分钟。这违背了我提高性能的目的。

然后我尝试在两个表上并行创建聚集索引,每个表在不同的会话中。这一次是最糟糕的:一个需要 12 分钟才能完成,另一个需要 25 分钟才能完成。

从我的测试结果来看,我最好的选择是回到第一方:使用表上的聚集索引顺序执行两个事务。

有没有人遇到过类似的情况?加快速度的最佳做法是什么?

【问题讨论】:

我没有什么可以支持的,但我猜你的第一个测试用例具有 I/O 顺序的优势......这意味着你磁盘上的磁头没有跳遍整个地方...在所有其他测试用例中,I / O是随机的,这意味着磁头为您的一个表写入一个块,然后跳转到磁盘上的另一个位置为另一个表,像那样来回移动......这就是为什么并行执行比顺序执行花费更长的时间。 删除 WITH(TABLOCK) 提示会提高并行插入速度吗? SRC_A 和 SRC_B 中的数据是否可能会命中聚集索引的不同区域或相同区域? 感谢您的回复。删除 WITH(TABLOCK) 将无济于事,因为这会使插入完全记录。而且这两张表完全不同。它们的簇键列完全不同。 我倾向于认为并行缓慢是由于磁盘磁头跳跃造成的,但没想到成本如此之大。我试图用谷歌搜索,但没有找到类似的情况和解决方案。 【参考方案1】:

在插入记录后创建聚集索引时,SQL 无论如何都必须在后台重新创建此表,因此将记录直接插入到已经存在聚集索引的表中会更快。 在插入时禁用任何非聚集索引并在之后再次启用它们,在填充的表上创建索引比为每个插入更新它们更快。记得在创建索引时将 Max DOP 选项设置为 0。

批量插入也比插入语句快得多。 我使用“SQL 服务器导入和导出向导”来复制大量数据,而且速度似乎更快(该向导使用批量语句)。如有必要,您可以尝试查找此向导使用的语句并自行运行。

【讨论】:

以上是关于并行创建 SQL Server 聚集索引的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server索引的创建与维护

SQL Server索引的创建与维护

SQL Server 使用没有主键的聚集索引创建表

sqlserver的索引

为啥在sql server中每张表只能创建一个聚集索引?

SQL Server 索引视图:无法创建聚集索引,因为选择列表包含聚合函数结果的表达式