如何针对大量插入调整具有许多非聚集索引的非常大的表?

poj*_*guy 2 sql-server

环境:

Windows Server 2019 上的 SQL Server 2019、由 TrueNAS 支持的 KVM、16 核、32 GB RAM。应用程序运行 50 个并行线程,所有线程都插入同一个大型表中。这种组合似乎不利于 SQL Server 架构

额外细节

  • 问题表既深又宽 - 20,000,000 行,超过 300 列和 40-50 个索引
  • 该应用程序使用 JDBC Batch API。由于行大小的原因,该特定表将分批插入 1,000 行。
  • 行大小更合理的表以 10,000 行为批量插入
  • 我无法分享实际的 DDL,但除了行很大之外(一个代理键 BIGINT ID 列、两个自然键 VARCHAR 列、300 个左右的货物列、0 个 BLOB/CLOB 列,然后 40-50 个),它非常普通索引)
  • 主键索引DDL为“create unique index mytable_pk on dbo.mytable (keycolumn);”
  • 唯一的其他唯一索引 DDL 是“在 dbo.mytable (division, itemnum) 上创建唯一索引 mytable_ndx1”;
  • 拥有该数据库的产品被数百名财富 2000 强客户使用,因此更改数据模型对于我或产品供应商来说都不是一个选择。

限制

  • 由于数据库最终是第三方的,因此我对其所做的任何更改都必须就位。一旦数据被插入其中,我就无法再访问它。
  • 该数据库由第三方现成应用程序拥有。
  • 主键是一个连续整数

观察和指标

在此过程的早期,我们遇到了 CPU 资源瓶颈。

一旦达到大约 1,000,000 行,我们就在锁存器上进行单线程处理,有时在锁存器上花费超过两秒,并且很少在锁存器上花费少于 500 毫秒。锁存和 IO 缓冲区等待都过多。CPU 使用率下降至 12% 左右。

在第二次测试中,我删除了所有索引并重新运行该作业。该作业的完成速度提高了 8 倍,显示 SQL Server 上的负载为零,并且应用程序上的 CPU 出现瓶颈,从 SQL Server 的角度来看,这非常好。

在阅读了 Microsoft 的文献后,我得出的结论是,该数据模型正在针对 SQL Server 的索引架构进行工作,以调整大规模插入。

我并不总是可以选择删除并重新创建索引。有没有办法调整表来分配 I/O

** 现在进入真正的问题 **

有没有一种方法可以在幕后调整 SQL Server 来分配 IO,以便在执行大量顺序数据插入时,索引中的顺序号而不是同一缓冲区中的顺序号?

Pau*_*ite 8

有几种众所周知的方法可以解决 SQL Server 中的最后一页插入争用问题。

解决 SQL Server 中最后一页插入 PAGELATCH_EX 争用的文档中介绍了其中的许多内容。总结该链接中的选项:

  1. 使用OPTIMIZE_FOR_SEQUENTIAL_KEY详细
  2. 将主键移出标识列
  3. 使主键成为非顺序列
  4. 添加非顺序值作为主键
  5. 使用 GUID 作为主键
  6. 使用表分区和具有哈希值的计算列
  7. 切换到内存中 OLTP

方法 7 还可以实现为内存中 OLTP 表,以通过定期批量移动到最终目标表来处理高速率的摄取。为了获得最高的并发性,请尽可能对内存表使用本机编译的代码(包括插入)。移动的频率和大小由您的要求决定。

正如另一个答案中提到的,延迟耐用性在许多情况下也可以提高刀片性能。

相关问答:解决周期性高位PAGELATCH_EX等待。最后一页争用?

尽管如此,您根本没有显示最后一页争用问题的证据。更有可能的是,您遇到与更新所有这些二级索引相关的问题以及实例内存不足,这意味着索引维护通常必须等待从存储中引入页面进行修改。您没有提到您看到的等待的闩锁类型,但我想它们会是PAGEIOLATCH_*

主要解决方案是大幅增加 SQL Server 缓冲池可用的内存,从而减少需要的 IO。如果做不到这一点,就需要更快的存储子系统。