क्या मैं इस क्वेरी को समानांतर में चलाने के लिए इसे रिफलेक्टर कर सकता हूं?


12

मेरे पास एक क्वेरी है जिसे हमारे सर्वर पर चलने में लगभग 3 घंटे लगते हैं - और यह समानांतर प्रसंस्करण का लाभ नहीं उठाता है। (लगभग 1.15 मिलियन रिकॉर्ड 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;

और ReplaceMultiwordएक प्रक्रिया के रूप में परिभाषित किया गया है:

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

ReplaceMultiwordएक समानांतर योजना बनाने से रोकने के लिए कॉल है ? क्या समानता को अनुमति देने के लिए इसे फिर से लिखने का एक तरीका है?

ReplaceMultiword अवरोही क्रम में चलता है क्योंकि कुछ प्रतिस्थापन दूसरों के लघु संस्करण हैं, और मैं चाहता हूं कि सबसे लंबा मैच सफल हो।

उदाहरण के लिए, 'जॉर्ज वाशिंगटन विश्वविद्यालय' और दूसरा 'वाशिंगटन विश्वविद्यालय' हो सकता है। अगर 'वाशिंगटन यूनिवर्सिटी' का मैच पहले होता, तो 'जॉर्ज' पीछे रह जाता।

क्वेरी योजना

तकनीकी रूप से मैं सीएलआर का उपयोग कर सकता हूं, मैं अभी परिचित नहीं हूं कि ऐसा कैसे किया जाए।


3
परिवर्तनीय असाइनमेंट ने केवल एक पंक्ति के लिए व्यवहार को परिभाषित किया है। SELECT @var = REPLACE ... ORDER BYनिर्माण कार्य की गारंटी नहीं है, जैसा कि आप उम्मीद करते हैं। उदाहरण कनेक्ट आइटम (Microsoft से प्रतिक्रिया देखें)। इसलिए, SQLCLR पर स्विच करने से सही परिणाम की गारंटी का अतिरिक्त फायदा होता है, जो हमेशा अच्छा होता है।
पॉल व्हाइट 9

जवाबों:


11

यूडीएफ समानता को रोक रहा है। यह उस स्पूल का कारण भी बन रहा है।

आप अपनी खोज और बदलने के लिए CLR और संकलित रेगेक्स का उपयोग कर सकते हैं। यह समानता को तब तक अवरुद्ध नहीं करता है जब तक आवश्यक गुण मौजूद नहीं होते हैं और संभवतः REPLACEप्रति फ़ंक्शन कॉल में 300 टीएसक्यूएल संचालन करने की तुलना में काफी तेज होगा ।

उदाहरण कोड नीचे है।

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; 

यह नीचे के रूप में एक सीएलआर यूडीएफ के अस्तित्व पर निर्भर करता है ( DataAccessKind.Noneइसका मतलब यह होना चाहिए कि स्पूल गायब हो जाता है और हैलोवीन सुरक्षा के लिए भी है और इसकी आवश्यकता नहीं है क्योंकि यह लक्ष्य तालिका तक नहीं पहुंचता है)।

using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Xml;

public partial class UserDefinedFunctions
{
    //TODO: Concurrency?
    private static readonly Dictionary<string, ReplaceSpecification> cachedSpecs = 
                        new Dictionary<string, ReplaceSpecification>();

    [SqlFunction(IsDeterministic = true,
                 IsPrecise = true,
                 DataAccess = DataAccessKind.None,
                 SystemDataAccess = SystemDataAccessKind.None)]
    public static SqlString ReplaceMultiWord(SqlString inputString, SqlXml replacementSpec)
    {
        //TODO: Implement something to drop things from the cache and use a shorter key.
        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;
        }

        string result = rs.GetResult(inputString.ToString());
        return new SqlString(result);
    }


    internal class ReplaceSpecification
    {
        internal ReplaceSpecification(XmlDocument doc)
        {
            Replacements = new Dictionary<string, string>();

            XmlElement root = doc.DocumentElement;
            XmlNodeList nodes = root.SelectNodes("x");

            string pattern = null;
            foreach (XmlNode node in nodes)
            {
                if (pattern != null)
                    pattern = pattern + "|";

                string find = node.Attributes["find"].Value.ToLowerInvariant();
                string replace = node.Attributes["replace"].Value;
                 //TODO: Escape any special characters in the regex syntax
                pattern = pattern + find;
                Replacements[find] = replace;
            }

            if (pattern != null)
            {
                pattern = "(?:" + pattern + ")";
                Regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            }


        }
        private Regex Regex { get; set; }

        private Dictionary<string, string> Replacements { get; set; }


        internal string GetResult(string inputString)
        {
            if (Regex == null)
                return inputString;

            return Regex.Replace(inputString,
                                 (Match m) =>
                                 {
                                     string s;
                                     if (Replacements.TryGetValue(m.Value.ToLowerInvariant(), out s))
                                     {
                                         return s;
                                     }
                                     else
                                     {
                                         throw new Exception("Missing replacement definition for " + m.Value);
                                     }
                                 });
        }
    }
}

मैंने अभी इसे बेंचमार्क किया है। प्रत्येक के लिए एक ही तालिका और सामग्री का उपयोग करते हुए, CLR ने 1,174,731 पंक्तियों को संसाधित करने के लिए 3: 03.51 लिया, और UDF ने 3: 16.21 लिया। इसने समय की बचत की। मेरे आकस्मिक पढ़ने में, ऐसा लगता है कि SQL सर्वर UPDATE प्रश्नों को समानांतर करने के लिए घृणास्पद है।
rsjaffe

@ शरज निराशाजनक। मुझे इससे बेहतर परिणाम की उम्मीद थी। डेटा का आकार क्या शामिल है? (सभी प्रभावित स्तंभों की डेटा लंबाई का योग)
मार्टिन स्मिथ

608 मिलियन वर्ण, 1.216 जीबी, प्रारूप NVARCHAR है। मैं whereरेगेक्स के साथ मैच के लिए एक परीक्षण का उपयोग करके एक खंड जोड़ने के बारे में सोच रहा था , क्योंकि अधिकांश लेखन अनावश्यक हैं - 'हिट' का घनत्व कम होना चाहिए, लेकिन मेरा सी # कौशल (मैं एक सी ++ लड़का हूं) नहीं था। मुझे वहाँ ले आओ। मैं एक प्रक्रिया की तर्ज पर सोच रहा था public static SqlBoolean CanReplaceMultiWord(SqlString inputString, SqlXml replacementSpec)जो वापस आ जाएगी, return Regex.IsMatch(inputString.ToString()); लेकिन मुझे उस रिटर्न स्टेटमेंट पर त्रुटियां मिलती हैं, जैसे कि `System.Text.RegularExpressions.Regex एक प्रकार है, लेकिन इसका उपयोग चर की तरह किया जाता है।
rsjaffe

4

नीचे पंक्ति : WHEREखंड में मानदंड जोड़ना और क्वेरी को चार अलग-अलग प्रश्नों में विभाजित करना, प्रत्येक क्षेत्र के लिए एक SQL सर्वर ने एक समानांतर योजना प्रदान करने की अनुमति दी और क्वेरी को अतिरिक्त रूप से WHEREउपबंध किए बिना 4X को तेजी से चलाया । परीक्षण के बिना प्रश्नों को चार में विभाजित करने से ऐसा नहीं हुआ। प्रश्नों को विभाजित किए बिना न तो परीक्षण को जोड़ा। परीक्षण के अनुकूलन से कुल रन समय 3 मिनट (मूल 3 घंटे से) कम हो गया।

मेरे मूल यूडीएफ को 1,174,731 पंक्तियों को संसाधित करने में 3 घंटे 16 मिनट का समय लगा, जिसमें 1.216 जीबी नावरच डेटा का परीक्षण किया गया। मार्टिन स्मिथ द्वारा अपने जवाब में प्रदान की गई CLR का उपयोग करते हुए, निष्पादन योजना अभी भी समानांतर नहीं थी और कार्य में 3 घंटे और 5 मिनट का समय लगा। सीएलआर, निष्पादन योजना समानांतर नहीं

पढ़ा है कि WHEREमानदंड एक UPDATEसमानांतर करने में मदद कर सकता है , मैंने निम्नलिखित किया। मैंने CLR मॉड्यूल में एक फ़ंक्शन जोड़ा, यह देखने के लिए कि क्या फ़ील्ड का regex से मेल है:

[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());
}

और, में internal class ReplaceSpecification, मैंने रेगेक्स के खिलाफ परीक्षण को निष्पादित करने के लिए कोड जोड़ा

    internal bool IsMatch(string inputString)
    {
        if (Regex == null)
            return false;
        return Regex.IsMatch(inputString);
    }

यदि सभी फ़ील्ड एक ही कथन में परीक्षण किए जाते हैं, तो SQL सर्वर कार्य को समानांतर नहीं करता है

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);

4 1/2 घंटे और अभी भी चल रहा है। निष्पादन योजना: टेस्ट जोड़ा, एकल बयान

हालाँकि, यदि फ़ील्ड्स को अलग-अलग स्टेटमेंट में अलग किया जाता है, तो एक समानांतर कार्य योजना का उपयोग किया जाता है, और मेरे सीपीयू का उपयोग धारावाहिक योजनाओं के साथ 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;

46 मिनट निष्पादित करने का समय। रो के आंकड़ों से पता चला कि लगभग 0.5% रिकॉर्ड्स में कम से कम एक रेगेक्स मैच था। निष्पादन योजना: यहाँ छवि विवरण दर्ज करें

अब, समय पर मुख्य ड्रैग WHEREक्लॉज था । मैंने तब CLR के रूप में लागू अहो-कोरासिक एल्गोरिथ्म केWHERE साथ क्लॉज में रेगेक्स टेस्ट को बदल दिया । इससे कुल समय 3 मिनट 6 सेकंड कम हो गया।

इसके लिए निम्न परिवर्तनों की आवश्यकता थी। अहो-कोरासिक एल्गोरिथ्म के लिए विधानसभा और कार्यों को लोड करें। WHEREक्लॉज को बदलें

WHERE  InProcess = 1 AND dbo.ContainsWordsByObject(ISNULL(FieldBeingTestedGoesHere,'x'), @ac) = 1; 

और पहले से पहले निम्नलिखित जोड़ें UPDATE

DECLARE @ac NVARCHAR(32);
SET @ac = dbo.CreateAhoCorasick(
  (SELECT NAMES FROM dbo.NamesMultiWord FOR XML RAW, root('root')),
  'en-us:i'
);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.