在只读副本上长时间运行的查询会占用主数据库上的时间


8

我有一个4节点AG设置,如下所示:

所有节点的VM硬件配置:

  • Microsoft SQL Server 2017企业版(RTM-CU14)(KB4484710)
  • 16个vCPU
  • 356 GB RAM(长话短说...)
  • 最大并行度:1(根据应用程序供应商的要求)
  • 并行成本阈值:50
  • 服务器最大内存(MB):338944(331 GB)

AG配置:

  • 节点1:主节点或同步提交不可读的辅助节点,配置为自动故障转移
  • 节点2:主节点或同步提交不可读的辅助节点,配置为自动故障转移
  • 节点3:具有异步提交的可读辅助集,配置为手动故障转移
  • 节点4:具有异步提交的可读辅助节点集,配置为手动故障转移

有疑问的查询:

此查询没有什么疯狂的,它提供了应用程序内各种队列中未完成工作项的摘要。您可以从下面的执行计划链接之一查看代码。

主节点上的执行行为:

在主要节点上执行时,执行时间通常约为1秒标记。这是执行计划,以下是从主节点从STATISTICS IO和STATISTICS TIME捕获的统计信息:

(347 rows affected)
Table 'Worktable'. Scan count 647, logical reads 2491, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'workitemlc'. Scan count 300, logical reads 7125, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulertask'. Scan count 1, logical reads 29, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'wfschedulertask'. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerservice'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerworkerpool'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'itemlc'. Scan count 1, logical reads 26372, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 500 ms,  elapsed time = 656 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

只读辅助节点上的执行行为:

在任一只读辅助节点(即节点3或节点4)上执行时,此查询使用相同的执行计划(这是一个不同的计划链接),并且显示大致相同的执行状态(例如,可能会有更多页面)扫描,因为这些结果总是在变化),但是除了CPU时间外,它们看起来非常相似。以下是从只读辅助节点从STATISTICS IO和STATISTICS TIME捕获的统计信息:

(347 rows affected)
Table 'Worktable'. Scan count 647, logical reads 2491, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'workitemlc'. Scan count 300, logical reads 7125, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulertask'. Scan count 1, logical reads 29, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'wfschedulertask'. Scan count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerservice'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'schedulerworkerpool'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'itemlc'. Scan count 1, logical reads 26372, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 55719 ms,  elapsed time = 56335 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

其他详情:

在执行此查询的同时sp_WhoIsActive,我也在Paul Randal的WaitingTasks.sql辅助脚本上运行了这两个脚本,但是我看不到发生任何等待,这实在令人沮丧:

在此处输入图片说明

这似乎也不是AG延迟的情况,因为“同步”状态实际上非常好:

--https://sqlperformance.com/2015/08/monitoring/availability-group-replica-sync

SELECT 
       ar.replica_server_name, 
       adc.database_name, 
       ag.name AS ag_name, 
       drs.is_local, 
       drs.synchronization_state_desc, 
       drs.synchronization_health_desc, 
       --drs.last_hardened_lsn, 
       --drs.last_hardened_time, 
       drs.last_redone_time, 
       drs.redo_queue_size, 
       drs.redo_rate, 
       (drs.redo_queue_size / drs.redo_rate) / 60.0 AS est_redo_completion_time_min,
       drs.last_commit_lsn, 
       drs.last_commit_time
FROM sys.dm_hadr_database_replica_states AS drs
INNER JOIN sys.availability_databases_cluster AS adc 
       ON drs.group_id = adc.group_id AND 
       drs.group_database_id = adc.group_database_id
INNER JOIN sys.availability_groups AS ag
       ON ag.group_id = drs.group_id
INNER JOIN sys.availability_replicas AS ar 
       ON drs.group_id = ar.group_id AND 
       drs.replica_id = ar.replica_id
ORDER BY 
       ag.name, 
       ar.replica_server_name, 
       adc.database_name;

在此处输入图片说明

该查询似乎是最严重的犯罪者。其他在主节点上也花费亚秒级时间的查询在辅助节点上可能花费1-5秒,并且这种行为虽然没有那么严重,但确实引起了问题。

最后,我还查看了服务器,并检查了诸如A / V扫描之类的外部进程,生成意外I / O的外部作业等,并且空手而归。我认为这不是由SQL Server进程之外的任何原因引起的。

问题:

我只是在中午的时候,已经有很长的一天了,所以我怀疑我这里缺少明显的东西。要么是因为我们配置不正确,要么是由于我们多次致电与该环境有关的供应商和MS,这是有可能的。

对于我的所有调查,我似乎都无法找到导致这种性能差异的原因。我希望在辅助节点上看到某种等待,但是什么也没有。如何进一步解决此问题以找出根本原因?有没有人以前见过这种行为,并找到了解决方法?

更新#1 将第三个节点(只读副本之一)的状态交换为不可读,然后又恢复为可读作为测试后,该副本仍由开放事务处理,所有客户端查询都将显示HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING等待。

运行DBCC OPENTRAN命令会产生以下结果:

Oldest active transaction:
    SPID (server process ID): 420s
    UID (user ID) : -1
    Name          : QDS nested transaction
    LSN           : (941189:33148:8)
    Start time    : May  7 2019 12:54:06:753PM
    SID           : 0x0
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

在中查找此SPID时sp_who2,将其显示为一个BACKGROUND进程,并将其QUERY STORE BACK列为命令。

虽然我们能够采取的TLog备份,我想我们正在运行到的类似的功能这个解决的错误,所以我打算与MS关于这一具体问题今天开了罚单。

根据该票证的结果,我将尝试根据Joe的建议捕获呼叫堆栈跟踪并查看我们的去向。

最终更新(问题自行解决)

在使查询存储事务的开放时间超过52小时(如上所示)之后,AG决定自动进行故障转移。在此之前,我确实做了一些其他指标。每此链接,由Sean提供的,有问题的数据库的人不得不专门为这个数据库非常大的版本库,特别是在一个点上我已经记录在1651360个页面reserved_page_count场和13210880的reserved_space_kb值。

根据ERRORLOGs,故障转移发生在5分钟的与QDS base transactionQDS nested transaction事务相关的事务强化失败之后。

在我的情况下,故障转移确实造成了大约10分钟的停机。该数据库的大小约为6TB,并且非常活跃,因此我认为这实际上非常好。当新的主节点在此期间处于联机状态时,没有客户端查询可以完成,因为它们都在等待QDS_LOADDB等待类型。

故障转移后,版本存储号减少为的176 reserved_page_count和的1408 reserved_space_kb。对辅助只读副本的查询也开始像从主副本运行一样快地执行,因此由于故障转移,行为似乎完全消失了。


如果您无法更改主数据库上未完成事务的长度或控制辅助数据库上的重击查询,那么将工作负载指向主数据库将缓解长期运行的问题-尽管可能会遇到其他与查询相关的典型问题。我不会说将副本设置为不可读以清除问题是正常的,但这是一种很好的故障排除技术。这完全取决于您是否能够/想要解决根本原因,或者只是在情况恶化时才解决症状。
Sean Gallardy-退休用户,

1
嗨,约翰-很好地回答了这个问题。只是想提及一下QDS_LOADDB-如果将来想避免这种情况,但仍保持Query Store处于打开状态,则可以使用Microsoft建议的这些跟踪标志。特别是7752将允许查询在查询存储初始化之前执行(因此您可能会错过一些查询,但您的数据库将启动)。
乔什·达内尔19'May

约翰(John)-您的工作负载是否存在非参数化,参数化差或临时性的情况?我已经看到了与临时类型工作负载有关的QDS的一些问题
AMtwo

@AMtwo是,命中数据库的绝大多数查询是在客户端生成的,并且未进行参数化(即,即席查询)。
约翰·埃斯布雷纳

@JoshDarnell跟踪标志7752看起来特别有用。谢谢你的提示!
约翰·埃斯布雷纳

Answers:


9

这个答案是Joe的答案的补充,因为我不能100%地确定它是版本存储库,但是到目前为止,有足够的证据表明这是问题的一部分。

当将辅助副本标记为可读时,首先需要获得版本控制信息的良好稳态,以便为辅助副本上的所有读取操作提供一个已知且良好的起点。当这等待过渡并且主数据库上仍然有未完成的事务时,这将显示为HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING并且也很好地表明主数据库确实经历了相当多的数据流失(或者至少有人进行了很长时间的未清事务,也不好)。事务打开的时间越长,数据更改越多,版本控制就越多。

二级副本通过在会话的幕后使用快照隔离来实现可读状态,即使您检查了会话信息,您也会看到它以默认的已提交读状态显示。由于快照隔离是乐观的,并且使用版本存储,因此所有更改都需要进行版本控制。当辅助节点上有许多正在运行(并且可能长时间运行)的查询而主节点上的数据流失率很高时,这种情况会加剧。通常,这仅在OLTP系统的几个表中体现出来,但它完全取决于应用程序和工作负载。

版本存储本身是经过几代度量的,并且在运行需要使用版本存储的查询时,将使用版本记录记录指针来指向该行的TempDB链。我说的是链,因为它是该行的版本列表,并且必须按顺序遍历整个链以基于事务的开始时间戳找到合适的版本,以便结果与给定时间的数据一致。

如果由于主副本和辅助副本上的事务长时间运行,版本存储为这些行提供了许多代,则这将导致查询运行的时间比平均时间更长,并且通常以更高的CPU形式出现,而其他所有项目似乎保持不变-例如执行计划,统计信息,返回的行等。链的遍历几乎是纯粹的cpu操作,因此,当链变得很长且返回的行数很高时,您会得到(不是线性的,但是可以关闭)查询的时间增加。

唯一可以做的是限制主数据库和辅助数据库上的事务长度,以确保版本数据库在TempDB中不会因为生成多代而变得太大。尝试清理版本存储的操作大约每分钟发生一次,但是清理要求不再需要同一代的所有版本,然后才能删除它,并且直到不再需要最旧的版本时,才能清理所有将来的版本。因此,长时间运行的查询可能导致无法有效清除许多未使用的世代。

将副本切换为可读模式或从可读模式切换出,也会清除版本存储,因为它不再可读。

还有其他项目也可能在起作用,但是考虑到当前数据和副本的反应方式,这似乎是最合理的。

TempDB版本控制DMV(不要与ADR版本控制混淆)。


针对进行查询时sys.dm_tran_version_store_space_usage,它会返回1651360作为我的reserved_pa​​ge_count值,并返回13210880作为有关数据库的我的reserved_space_kb值。迹象看起来不错,尽管您已确定此问题。再次感谢您的详细解释!
约翰·埃斯布雷纳

1
我大约有103%的人确定您正确地提出了此问题。我对问题进行了一些更新,但非常感谢您的见解!
约翰·埃斯布雷纳

8

免责声明:我对可用性组一无所知,但我对故障排除似乎应该使用的CPU多一些了解。

您有一个CPU问题,因为您使用的过多。关于等待的重要一件事是几乎所有等待都不是CPU繁忙。当工作程序进入等待状态时,它已经屈服并且不再在SQLOS的调度程序上运行。因此,如果您的MAXDOP 1查询具有以下运行统计信息:

CPU时间= 55719毫秒,经过的时间= 56335毫秒。

您查询的CPU使用率几乎达到99%。为什么应该为该查询提供有意义的等待统计信息?如果您有一些CPU忙等待(例如外部等待或抢占等待),则可能会看到一些,但这也不保证。最重要的是,等待统计信息在这里可能没有帮助。

有一些事情需要按大致顺序检查(顺序取决于您对环境的了解):

  • 辅助服务器是否进行任何昂贵的监视,例如扩展事件,跟踪或概要分析?
  • 辅助服务器的硬件是否与主服务器大致匹配?
  • 辅助服务器是否存在任何配置或软件问题?
  • 是否有重大等待或闩锁?可能不适用于您的查询,但仍可能提供线索。
  • 有明显的自旋锁吗?
  • 在SQL Server中是否还可以检查其他DMV或可以提供线索的内容?您提到可用性组可能是问题的关键部分。
  • ETW追踪告诉您什么?
  • 您有什么样的支持协议?

以上大部分内容涵盖了各种博客文章和文档,但我将继续介绍ETW跟踪。如果您想知道SQL Server为什么对一个特定查询使用这么多的CPU,并且可以访问主机,则可以随时进行ETW跟踪以查看调用堆栈并查看各种调用堆栈在处理多少CPU。换句话说,如果您知道如何询问,主机操作系统很高兴告诉您正在使用什么CPU。进行ETW跟踪的常用方法包括Windows Performance RecorderPerfView

弄清这些结果需要深入的内部知识,并且很容易得出错误的结论。在许多情况下,最好收集原始数据并请专家进行查看。执行跟踪时,您希望在SQL Server中进行尽可能少的活动。下面是在此处发布的一些答案,这些答案使用ETW跟踪得出有关SQL Server的结论:

我怀疑在您的情况下,如果您能够在运行45秒查询的同时收集呼叫堆栈,则会获得一些有关问题性质的非常有用的线索。


5

由于该问题已自动解决,因此我只能推测其原因(押韵不是故意的)。基于Sean的帖子以及开放的查询存储事务似乎是导致我的版本存储大小增加的根本原因(例如,导致HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING等待的原因)的事实,我只能假设查询存储参与了以下行为:提出了。该数据库较大(〜6TB),非常活跃,并且命中该数据库的大部分查询都是在客户端生成的而不是参数化的(即即席查询),因此我认为查询存储不适合提供在这种情况下有很多用途。因此,在以后的维护窗口中,我们将在此环境中禁用查询存储,此后,我怀疑我们不会再看到此行为。

我们确实向Microsoft开了张罚单,但由于在解决问题之前,我们无法通过PSSDIAG跟踪等进行任何详细分析,因此时机不利于我们。我希望他们能够进行一些本地化测试并复制此问题,以防万一这是我们遇到的错误。如果确定了关于更永久解决方案的任何进一步更新,我也将确保也更新此答案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.