设想
假设我有一个带有 4 个套接字的 SQL Server,每个套接字有 1 个 NUMA 节点。每个插槽有 4 个物理内核。总共有 512 GB 的内存,因此每个 NUMA 节点有 128 GB 的 RAM。
一个密钥表被加载到第一个 NUMA 节点中。
问题
假设我们从该表中读取了大量流量。如果拥有 NUMA 节点的套接字的所有物理核心都具有 100% 的 CPU 使用率,这是否会对来自其他套接字的非本地 NUMA 访问的成本产生负面影响?或者另一方面,非本地 NUMA 访问的成本与该套接字的繁忙程度无关?
我希望我的问题是有道理的。如果没有,请告诉我,我会尽力澄清。
背景
上周我们的生产服务器出现数据库问题,我们处理的一些业务似乎比其他业务受到的影响更大。我们有一些逻辑读取时间超过 1 分钟的查询。我们查看了大约 60% 的整体 CPU 利用率。我们没有查看特定于套接字的 CPU 指标。I/O 指标是平均水平。
一个沉重的问题 :-) 我将概述一些涉及的因素。在任何给定的上下文中,这些因素和其他因素可能会发生变化并产生有趣的结果。
对不起,我没能把这个缩短很多……
累计 CPU 毫秒与逻辑 IO
我经常使用逻辑 IO(或性能监视器术语“缓冲池页面查找”)与 CPU 利用率的图表,以衡量工作负载的 CPU 效率并寻找容易发生自旋锁的情况。
但是 SQL Server 会通过除页面查找和自旋锁之外的大量活动来积累 CPU 时间:
许多其他活动会消耗大量 CPU 时间,而不会反映在页面查找中。
在我观察到的工作负载中,这些“非逻辑 IO 密集型但占用 CPU”的活动中最主要的是排序/散列活动。
这是有道理的:考虑一个针对没有非聚集索引的哈希表的两个查询的人为示例。这两个查询具有相同的结果集,但其中一个结果集是完全无序的,而第二个结果集按多个选定列排序。第二个查询预计会消耗更多的 CPU 时间,即使它会引用缓冲池中相同数量的页面。
有关工作区内存以及已使用多少已授予工作区的更多信息,请参阅这些帖子:
http://sql-sasquatch.blogspot.com/2015/08/sql-server-grantedreservedstolen_4.html
http://sql-sasquatch.blogspot.com/2015/08/sql-server-workspace-memory-with-twist.html
http://sql-sasquatch.blogspot.com/2015/03/resource-governor-to-restrict-max-query.html
SQL Server 逻辑内存节点与物理 NUMA 节点对齐
SQL Server(因为合并了它的 NUMA 感知策略)默认情况下为服务器上的每个 NUMA 节点创建一个 SQLOS 内存节点。随着内存分配的增长,每个分配都由一个 SQLOS 内存节点控制。
理想情况下,SQLOS 内存节点与物理 NUMA 节点完全对齐。也就是说,每个 SQLOS 内存节点都包含来自单个 NUMA 节点的内存,没有其他 SQLOS 内存节点也包含来自同一 NUMA 节点的内存。
然而,理想情况并非总是如此。
以下 CSS SQL Server Engineers 博客文章(也包含在 Kin 的回复中)详细说明了可能导致 SQLOS 内存节点持续进行跨 NUMA 节点内存分配的行为。发生这种情况时,性能影响可能是毁灭性的。
对于持久性跨 NUMA 节点引用的特别痛苦的情况,已经有一些修复。除了这两个之外,可能还有其他人:
修复:在 SQL Server 2012 或 SQL Server 2014 中处理外部页面期间,NUMA 环境中出现性能问题
修复:NUMA 环境中的 SQL Server 性能问题
工作区内存分配期间的自旋锁争用
这是它开始变得有趣的地方。我已经描述了工作区内存中的排序和散列工作会消耗 CPU,但不会反映在 bpool 查找数字中。
自旋锁争用是这种特殊乐趣的另一层。当内存从缓冲池中被窃取并分配用于查询内存授权时,内存访问将使用自旋锁序列化。默认情况下,这发生在 NUMA 节点级别的资源分区上。因此,在使用工作区内存窃取内存时,同一 NUMA 节点上的每个查询都可能会遇到自旋锁争用。非常重要的是要注意:这不是“每次查询一次”的争用风险,如果争用点是在实际授权时就会有。更确切地说,它是在内存被授予时被盗 - 因此,如果它使用大部分授予,具有非常大的内存授予的查询将有很多自旋锁争用的机会。
跟踪标志 8048 通过在核心级别进一步划分资源,在缓解这种争用方面做得很好。
Microsoft 表示“如果每个插槽有 8 个或更多内核,请考虑跟踪标志 8048”。但是......实际上并不是每个插槽有多少个核心(只要有多个),而是在单个 NUMA 节点上完成的工作中有多少竞争机会。
在粘合的 AMD 处理器上(每个插槽 12 个内核,每个插槽 2 个 NUMA 节点)每个 NUMA 节点有 6 个内核。我看到一个系统有 4 个这样的 CPU(所以 8 个 NUMA 节点,每个节点 6 个内核)在自旋锁护送中被阻塞,直到启用跟踪标志 8048。
我已经看到这种自旋锁争用拖累了小至 4 个 vCPU 的 VM 的性能。跟踪标志 8048 在这些系统上启用时做了它应该做的事情。
考虑到仍然有一些 4 核心频率优化的 CPU,在适当的工作负载下,它们也会从跟踪标志 8048 中受益。
CMEMTHREAD 等待跟踪标志 8048 解除的自旋锁争用类型。但请注意:CMEMTHREAD 等待是一个确凿的症状,而不是这个特定问题的根本原因。我见过具有高 CMEMTHREAD“等待启动”的系统,其中跟踪标志 8048 和/或 9024 在部署中被延迟,因为累积的 CMEMTHREAD 等待时间相当短。对于自旋锁,累积的等待时间通常是错误的。相反,您想要查看浪费的 CPU 时间 - 主要由自旋本身表示,其次由代表潜在不必要的上下文切换的关联等待表示。
调度程序的任务分配
在 NUMA 系统上,假设没有与特定 NUMA 节点相关联的连接端点,连接被分配到 NUMA 节点(好吧——实际上是与它们关联的 SQLOS 调度程序组)循环。如果会话执行并行查询,则强烈倾向于使用来自单个 NUMA 节点的工作程序。嗯...考虑一个 4 NUMA 节点服务器,其复杂查询分为 4 个路径,默认为 0 MAXDOP。即使查询仅使用 MAXDOP 工作线程,NUMA 节点上的每个逻辑 CPU 也会有 4 个工作线程。但是在复杂的计划中有 4 条路径——所以 NUMA 节点上的每个逻辑 CPU 上可以有 16 个工作者——全部用于单个查询!
这就是为什么有时您会看到一个 NUMA 节点在努力工作而其他节点却在游手好闲。
任务分配还有其他一些细微差别。但主要的收获是 CPU 繁忙不一定会均匀分布在 NUMA 节点上。(也很高兴认识到 bpool 页面插入(读取或第一页写入)将进入与工作程序所在的调度程序关联的 SQLOS 内存节点中的 bpool。被盗页面将优先来自“本地”SQLOS 内存节点也是。
我发现将 maxdop 从 0 调整到不超过 8 是有帮助的。根据工作负载配置文件(主要是 imo 并发预期的潜在长时间运行的查询的数量),一直到 MAXDOP=2 可能是有保证的。
调整并行度的成本阈值也可能有帮助。我工作的系统往往会被高成本查询消耗并且很少遇到低于 50 或 100 的计划,因此我通过调整 maxdop(通常在工作负载组级别)比调整成本阈值更有吸引力。
具有 8 个 NUMA 节点的 SQL Server PLE
在这篇文章中,讨论了围绕工作区内存的自旋锁争用和不均匀的任务分配的组合。看 - 这些东西真的交织在一起 :-)
40 个并发 SQL Server 并行查询 +(2 个套接字 * 每个套接字 10 个内核)= spinlock convoy
bpool中的相关数据放置
这是我认为在处理 NUMA 服务器时最直观的情况。最典型的是,它对工作负载性能也不是非常重要。
如果该表被读入 NUMA 节点 3 上的 bpool,然后 NUMA 节点 4 上的查询扫描该表,执行跨 NUMA 节点的所有 bpool 查找,会发生什么情况?
Linchi Shea 发表了一篇关于这种性能影响的精彩文章:
跨 NUMA 节点访问内存会导致少量额外的内存延迟。我确信有一些工作负载需要消除额外的基本内存延迟以获得最佳性能——这在我使用的系统上不是问题。
但是 - 跨节点访问也带来了另一个可能会饱和的传输点。如果活动太多以至于 NUMA 节点之间的内存带宽饱和,节点之间的内存延迟将会增加。同样的工作将需要额外的 CPU 周期。
再次重申 - 我确信存在内存带宽是一个关键考虑因素的工作负载。不过,对于我的系统,我列出的其他考虑因素更为重要。
物理内存放置
这个很少见,但当它重要时,它真的很重要。在大多数服务器上,内存安装几乎自然会在 NUMA 节点之间保持平衡。但在某些情况下,需要特别注意跨节点平衡内存。如果内存以不平衡的方式插入,则某些系统的性能绝对会被破坏。不过,这是一劳永逸的。在经过数月的生产服务后很少发现这样的问题,而不是在第一个真正忙碌的一天之后发现这样的问题:-)
大结局!
其他人指出,糟糕的计划选择(可能是由于过时的统计数据)可能会导致您所看到的症状。根据我的经验,情况并非如此。糟糕的计划很容易使查询花费比预期更长的时间——但通常是因为执行了比必要更多的逻辑 IO。或者由于溢出到 tempdb。观察服务器时,tempdb 的大量溢出应该很明显 - 而不是高 CPU,人们会期望与溢出相关的磁盘写入有可测量的等待时间。
相反,如果您观察到的情况与 NUMA 相关,我希望它是上面列举的因素的组合,主要是:
使用工作区内存(不会显示在逻辑 IO 计数中)
由于持续的外部内存条件,这可能是跨 NUMA 节点(如果是这种情况,请查找相关修复程序)
并且每次针对授权进行分配时都可能在 NUMA 节点内引发自旋锁争用(使用 T8048 修复)
并且可能由工作人员在其他并行查询工作人员过载的逻辑 CPU 上执行(根据需要调整并行度的 maxdop 和/或成本阈值)
(请使用(系统内部实用程序)输出更新您的问题,
coreinfo -v
以更好地了解您的 CPU/套接字和 NUMA 分布)在我看来,你在错误的树上吠叫。SQL Server
NUMA
知道。执行跨 NUMA 内存访问的性能损失要小得多。您还可以使用此查询来查看您拥有多少NUMA
个节点以及分配给哪些 CPU 和内核NUMA
:或者有多少
NUMA
:当您由于过时的统计信息而生成错误的查询计划时,通常会发生这种情况。确保您更新了统计信息并且正确地对索引进行了碎片整理。
此外,您需要将 MAXDOP 设置为更合理的值以避免工作线程饥饿。
将
cost threshold of parallelism
默认值 5 设置为良好的起始值,例如 45,然后监控该值并根据您的环境进行调整。如果您正在运行大量临时查询,请打开(设置为 1)
optimize for ad hoc workloads
以防止计划缓存膨胀。谨慎使用:如果您在每个 NUMA 节点有超过 8 个 CPU 的较新机器上运行 SQL Server 2008/2008 R2,则可以使用T8048 ,如果您使用的是 SQL Server 2012 或 2014 ,则可以使用修补程序。
强烈建议您开始收集有关数据库服务器实例的等待统计信息。
参考:工作原理:SQL Server(NUMA 本地、外部和离开内存块)
纯粹从硬件的角度来看,从 Nehalem 架构开始的主内存管理是由集成内存控制器管理的,它位于 CPU 芯片的“非核心”部分,与实际核心所在的部分分开,因为内存有效地“连接”到每个 CPU,外部内存访问 AFAIK 是通过快速路径互连(再次从 Nehalem 开始),因此我会说本地 NUMA 节点上的 CPU 内核饱和不应影响对该内存的远程访问。
您可能会发现此链接很有用:
http://cs.nyu.edu/~lerner/spring10/projects/NUMA.pdf
克里斯