我可以重构此查询以使其并行运行吗?

rsj*_*ffe 12 performance sql-server parallelism sql-server-2016 query-performance

我有一个查询在我们的服务器上运行大约需要 3 个小时——而且它没有利用并行处理。(大约 115 万条记录dbo.Deidentified, 300 条记录dbo.NamesMultiWord)。服务器可以访问 8 个内核。

  UPDATE dbo.Deidentified 
     WITH (TABLOCK)
  SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml),
      DE461 = dbo.ReplaceMultiWord(DE461),
      DE87 = dbo.ReplaceMultiWord(DE87),
      DE15 = dbo.ReplaceMultiWord(DE15)
  WHERE InProcess = 1;
Run Code Online (Sandbox Code Playgroud)

ReplaceMultiword是一个过程定义为:

SELECT @body = REPLACE(@body,Names,Replacement)
 FROM dbo.NamesMultiWord
 ORDER BY [WordLength] DESC
RETURN @body --NVARCHAR(MAX)
Run Code Online (Sandbox Code Playgroud)

是呼吁ReplaceMultiword阻止形成平行计划吗?有没有办法重写它以允许并行?

ReplaceMultiword 以降序运行,因为某些替换是其他替换的简短版本,我希望最长的匹配成功。

例如,可能有“乔治华盛顿大学”和另一个来自“华盛顿大学”。如果“华盛顿大学”比赛是第一场,那么“乔治”就会被甩在后面。

查询计划

从技术上讲,我可以使用 CLR,只是我不熟悉如何使用。

Mar*_*ith 11

UDF 正在阻止并行性。它也是导致该线轴的原因。

您可以使用 CLR 和编译后的正则表达式来进行搜索和替换。只要存在所需的属性,它就不会阻止并行性并且可能比REPLACE每个函数调用执行 300 个 TSQL操作快得多。

示例代码如下。

DECLARE @X XML = 
(
    SELECT Names AS [@find],
           Replacement  AS [@replace]
    FROM  dbo.NamesMultiWord 
    ORDER BY [WordLength] DESC
    FOR XML PATH('x'), ROOT('spec')
);

UPDATE dbo.Deidentified WITH (TABLOCK)
SET    IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X),
       DE461 = dbo.ReplaceMultiWord(DE461, @X),
       DE87 = dbo.ReplaceMultiWord(DE87, @X),
       DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE  InProcess = 1; 
Run Code Online (Sandbox Code Playgroud)

这取决于 CLR UDF 的存在,如下所示(这DataAccessKind.None应该意味着假脱机消失以及用于万圣节保护的假脱机并且不需要,因为它不访问目标表)。

DECLARE @X XML = 
(
    SELECT Names AS [@find],
           Replacement  AS [@replace]
    FROM  dbo.NamesMultiWord 
    ORDER BY [WordLength] DESC
    FOR XML PATH('x'), ROOT('spec')
);

UPDATE dbo.Deidentified WITH (TABLOCK)
SET    IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X),
       DE461 = dbo.ReplaceMultiWord(DE461, @X),
       DE87 = dbo.ReplaceMultiWord(DE87, @X),
       DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE  InProcess = 1; 
Run Code Online (Sandbox Code Playgroud)


rsj*_*ffe 4

底线:向子句添加条件WHERE并将查询拆分为四个单独的查询,每个字段一个查询,允许 SQL Server 提供并行计划,并使查询运行速度比没有在子句中进行额外测试的情况下快 4 倍WHERE。在没有测试的情况下将查询分成四个并不能做到这一点。在不拆分查询的情况下添加测试也没有效果。优化测试将总运行时间从原来的 3 小时减少到 3 分钟。

我的原始 UDF 花费了 3 小时 16 分钟来处理 1,174,731 行,测试了 1.216 GB 的 nvarchar 数据。使用Martin Smith在回答中提供的CLR,执行计划仍然不并行,任务花费了3小时5分钟。 CLR,执行计划不并行

阅读了该WHERE标准可以帮助推动UPDATE并行之后,我做了以下事情。我向 CLR 模块添加了一个函数,以查看该字段是否与正则表达式匹配:

[SqlFunction(IsDeterministic = true,
         IsPrecise = true,
         DataAccess = DataAccessKind.None,
         SystemDataAccess = SystemDataAccessKind.None)]
public static SqlBoolean CanReplaceMultiWord(SqlString inputString, SqlXml replacementSpec)
{
    string s = replacementSpec.Value;
    ReplaceSpecification rs;
    if (!cachedSpecs.TryGetValue(s, out rs))
    {
        var doc = new XmlDocument();
        doc.LoadXml(s);
        rs = new ReplaceSpecification(doc);
        cachedSpecs[s] = rs;
    }
    return rs.IsMatch(inputString.ToString());
}
Run Code Online (Sandbox Code Playgroud)

并且,在 中internal class ReplaceSpecification,我添加了代码来针对正则表达式执行测试

    internal bool IsMatch(string inputString)
    {
        if (Regex == null)
            return false;
        return Regex.IsMatch(inputString);
    }
Run Code Online (Sandbox Code Playgroud)

如果在一条语句中测试所有字段,SQL Server 不会并行化工作

UPDATE dbo.DeidentifiedTest
SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X),
    DE461 = dbo.ReplaceMultiWord(DE461, @X),
    DE87 = dbo.ReplaceMultiWord(DE87, @X),
    DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE InProcess = 1
    AND (dbo.CanReplaceMultiWord(IndexedXml, @X) = 1
    OR DE15 = dbo.ReplaceMultiWord(DE15, @X)
    OR dbo.CanReplaceMultiWord(DE87, @X) = 1
    OR dbo.CanReplaceMultiWord(DE15, @X) = 1);
Run Code Online (Sandbox Code Playgroud)

执行时间超过 4 1/2 小时并且仍在运行。执行计划: 添加测试,单个语句

然而,如果这些字段被分成单独的语句,则使用并行工作计划,并且我的 CPU 使用率从串行计划的 12% 变为并行计划(8 核)的 100%。

UPDATE dbo.DeidentifiedTest
SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X)
WHERE InProcess = 1
    AND dbo.CanReplaceMultiWord(IndexedXml, @X) = 1;

UPDATE dbo.DeidentifiedTest
SET DE461 = dbo.ReplaceMultiWord(DE461, @X)
WHERE InProcess = 1
    AND dbo.CanReplaceMultiWord(DE461, @X) = 1;

UPDATE dbo.DeidentifiedTest
SET DE87 = dbo.ReplaceMultiWord(DE87, @X)
WHERE InProcess = 1
    AND dbo.CanReplaceMultiWord(DE87, @X) = 1;

UPDATE dbo.DeidentifiedTest
SET DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE InProcess = 1
    AND dbo.CanReplaceMultiWord(DE15, @X) = 1;
Run Code Online (Sandbox Code Playgroud)

执行时间46分钟。行统计显示大约 0.5% 的记录至少有一个正则表达式匹配。执行计划: 在此输入图像描述

现在,拖延时间的主要因素是该WHERE条款。WHERE然后,我用作为 CLR 实现的Aho-Corasick 算法替换了子句中的正则表达式测试。这将总时间减少到 3 分 6 秒。

这需要进行以下更改。加载 Aho-Corasick 算法的程序集和函数。将条款更改WHERE

WHERE  InProcess = 1 AND dbo.ContainsWordsByObject(ISNULL(FieldBeingTestedGoesHere,'x'), @ac) = 1; 
Run Code Online (Sandbox Code Playgroud)

并在第一个之前添加以下内容UPDATE

DECLARE @ac NVARCHAR(32);
SET @ac = dbo.CreateAhoCorasick(
  (SELECT NAMES FROM dbo.NamesMultiWord FOR XML RAW, root('root')),
  'en-us:i'
);
Run Code Online (Sandbox Code Playgroud)