存储过程调优帮助

AW_*_*DBA 3 sql-server availability-groups query-performance performance-tuning

实际计划:https://www.brentozar.com/pastetheplan/? id=SJviG_y1h

我正在寻找有关从哪里开始调整上述存储过程的建议。我们需要它在尽可能接近 1 秒的时间内完成,但目前在生产中最多需要 40 秒。SP 似乎会根据服务器工作负载而波动,因此需要它尽可能高效地运行。在 UAT 没有负载的情况下,针对同一数据库的副本,它在 5 秒内运行。

来自 Brent Ozar 的 sp_blitzcache 的警告包括:并行、计划警告、参数嗅探、隐式转换、函数连接、琐碎计划、未使用的内存授予、最近 4 小时创建的计划、多行表假脱机、非 SARGable

它在 Always On 可用性组数据库的主副本上运行(2 个节点设置,在辅助副本上启用同步提交和只读)。我们无法创建任何额外的索引。

SQL 版本为: Microsoft SQL Server 2017 (RTM-CU29) (KB5010786) - 14.0.3436.1 (X64) Enterprise Edition:Windows Server 2019 Standard 10.0(内部版本 17763:)(虚拟机管理程序)上基于核心的许可(64 位)

其他值得注意的配置:

  • SQL 最大内存设置为 92GB
  • 服务器有 8 个 CPU(全部位于 1 个 numa 节点上)
  • CTFP设置为50
  • MAXDOP 设置为 4
  • 主数据库启用了 RCSI

J.D*_*.D. 5

潜在的问题

快速浏览一下执行计划和代码正在执行的操作,看起来主要问题之一是您的JOIN子句[maxims_prod].[dbo].core_patient_c_identifi

LEFT JOIN [maxims_prod].[dbo].core_patient_c_identifi alt 
  ON cp.id = alt.id 
  AND alt.lkp_c_ty = CASE @SourceSystem WHEN 'Rio' THEN 6942 
             WHEN 'Trakcare' THEN 6999
             END
Run Code Online (Sandbox Code Playgroud)

具体来说CASE,该语句使谓词过于复杂,这似乎会导致对更大(+100 万行)表进行多次索引扫描。


一种可能的修复方法

解决这个问题的一种可能的方法是在变量上分支代码@SourceSystem,因为无论如何这都是一个存储过程。在加入 的主查询之前[maxims_prod].[dbo].core_patient_c_identifi,您可以创建以下分支以将数据具体化到临时表:

-- Don't use SELECT *, list out the columns explicitly instead. I'm only doing it because I don't have your table schema to reference
DROP TABLE IF EXISTS #core_patient_c_identifi;
SELECT TOP 0 *
INTO #core_patient_c_identifi
FROM [maxims_prod].[dbo].core_patient_c_identifi;

IF (@SourceSystem = `Rio`)
BEGIN
    INSERT INTO #core_patient_c_identifi
    SELECT *
    FROM [maxims_prod].[dbo].core_patient_c_identifi
    WHERE alt.lkp_c_ty = 6942
END
ELSE IF (@SourceSystem = `Trakcare`)
BEGIN
    INSERT INTO #core_patient_c_identifi
    SELECT *
    FROM [maxims_prod].[dbo].core_patient_c_identifi
    WHERE alt.lkp_c_ty = 6999
END
Run Code Online (Sandbox Code Playgroud)

那么JOIN主查询中的子句只需:

LEFT JOIN #core_patient_c_identifi alt
    ON cp.id = alt.id
Run Code Online (Sandbox Code Playgroud)

另一个危险的谓词

在查询的最后,您有以下谓词:

WHERE AlternativeLocalPtId = '' -- return all rows if no 3a match is found
    OR AlternativeLocalPtId = Matched_LocalPtId
Run Code Online (Sandbox Code Playgroud)

由于两个原因,这可能存在问题。第一个存在ORs 也可能会阻碍谓词的最佳搜索。虽然这个并不太复杂,所以可能不太关心。

更大的问题是该AlternativeLocalPtId列是先前子查询中带有 XML 解析表达式的内联相关子查询的别名。这使得上述谓词比我刚才用来描述它的词更加复杂:

(
    (SELECT
         CASE WHEN Match_type LIKE '%3a%' 
             THEN Matched_LocalPtId END [text()]
     FROM #MATCHED_RESULT MATCHTYPE2
     WHERE MATCHTYPE.MessageControlID  = MATCHTYPE2.MessageControlID    
     ORDER BY Match_type
     FOR XML PATH (''),TYPE).value('.','NVARCHAR(200)')
) AlternativeLocalPtId
Run Code Online (Sandbox Code Playgroud)

可能的修复

在这里,物化可能又成为你的朋友了。您可以仅提取查询的相关部分(可能是整个主查询本身)来构建 的表达式AlternativeLocalPtId,并将其插入到临时表中。然后按物化列过滤临时表AlternativeLocalPtId

我尝试写出该解决方案的代码示例,但查询太冗长,我无法在此处剥离和修改(我从手机上写下答案)。但此时你应该明白了。