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 位)
其他值得注意的配置:
潜在的问题
快速浏览一下执行计划和代码正在执行的操作,看起来主要问题之一是您的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。
我尝试写出该解决方案的代码示例,但查询太冗长,我无法在此处剥离和修改(我从手机上写下答案)。但此时你应该明白了。