खुद को गतिरोध बयान मर्ज


22

मेरे पास निम्न प्रक्रिया है (SQL Server 2008 R2):

create procedure usp_SaveCompanyUserData
    @companyId bigint,
    @userId bigint,
    @dataTable tt_CoUserdata readonly
as
begin

    set nocount, xact_abort on;

    merge CompanyUser with (holdlock) as r
    using (
        select 
            @companyId as CompanyId, 
            @userId as UserId, 
            MyKey, 
            MyValue
        from @dataTable) as newData
    on r.CompanyId = newData.CompanyId
        and r.UserId = newData.UserId
        and r.MyKey = newData.MyKey
    when not matched then
        insert (CompanyId, UserId, MyKey, MyValue) values
        (@companyId, @userId, newData.MyKey, newData.MyValue);

end;

CompanyId, UserId, MyKey लक्ष्य तालिका के लिए मिश्रित कुंजी बनाता है। CompanyId पैरेंट टेबल की एक विदेशी कुंजी है। इसके अलावा, एक गैर-संकुल सूचकांक है CompanyId asc, UserId asc

इसे कई अलग-अलग थ्रेड्स से कहा जाता है, और मैं इसे एक ही स्टेटमेंट को कॉल करने वाली विभिन्न प्रक्रियाओं के बीच लगातार गतिरोध प्राप्त कर रहा हूं। मेरी समझ यह थी कि दौड़ की स्थिति की त्रुटियों को सम्मिलित / अद्यतन करने से रोकने के लिए "(होल्डलॉक)" आवश्यक था।

मुझे लगता है कि दो अलग-अलग धागे पंक्तियों (या पृष्ठों) को अलग-अलग क्रम में बंद कर रहे हैं जब वे बाधाओं को मान्य कर रहे हैं, और इस प्रकार गतिरोध हैं।

क्या यह एक सही धारणा है?

इस स्थिति को हल करने का सबसे अच्छा तरीका क्या है (यानी कोई गतिरोध, बहु-थ्रेडेड प्रदर्शन पर न्यूनतम प्रभाव)?

क्वेरी योजना छवि (यदि आप एक नए टैब में छवि देखते हैं, तो यह पठनीय है। छोटे आकार के लिए क्षमा करें।)

  • सबसे अधिक 28 पंक्तियाँ @datatable में हैं।
  • मैंने कोड के माध्यम से वापस पता लगाया है, और मैं कहीं भी नहीं देख सकता कि हम यहां लेनदेन शुरू करते हैं।
  • विदेशी कुंजी केवल हटाए जाने पर कैस्केड करने के लिए सेट की गई है, और मूल तालिका से कोई हटाए नहीं गए थे।

जवाबों:


12

ठीक है, एक दो बार में सब कुछ देखने के बाद, मुझे लगता है कि आपकी मूल धारणा सही थी। शायद यहाँ क्या हो रहा है:

  1. MERGE का MATCH हिस्सा मैचों के लिए इंडेक्स की जाँच करता है, उन पंक्तियों / पृष्ठों को पढ़ता है जो इसे जाता है।

  2. जब यह एक मैच के बिना एक पंक्ति है, तो यह पहली बार नए सूचकांक पंक्ति सम्मिलित करने का प्रयास करेगा, इसलिए यह एक पंक्ति / पृष्ठ लिखने-लॉक का अनुरोध करेगा ...

लेकिन अगर किसी अन्य उपयोगकर्ता ने भी उसी पंक्ति / पृष्ठ पर चरण 1 में प्रवेश किया है, तो पहला उपयोगकर्ता अपडेट से अवरुद्ध हो जाएगा, और ...

यदि दूसरे उपयोगकर्ता को भी उसी पृष्ठ पर सम्मिलित करने की आवश्यकता है, तो वे एक गतिरोध में हैं।

AFAIK, केवल 100% होने का एक ही (सरल) तरीका है कि आप इस प्रक्रिया के साथ गतिरोध प्राप्त नहीं कर सकते हैं और यह कि MERGE में एक TABLOCKX संकेत जोड़ना होगा, लेकिन इसका प्रदर्शन पर वास्तव में बुरा प्रभाव पड़ेगा।

यह है संभव है कि इसके बजाय एक TABLOCK संकेत जोड़ना आपके प्रदर्शन पर बड़ा प्रभाव डाले बिना समस्या को हल करने के लिए पर्याप्त होगा।

अंत में, आप PAGLOCK, XLOCK या PAGLOCK और XLOCK दोनों को जोड़ने का प्रयास कर सकते हैं। फिर कि हो सकता है काम और प्रदर्शन हो सकता है बहुत भयानक नहीं हो। आपको इसे देखने की कोशिश करनी होगी।


क्या आपको लगता है कि स्नैपशॉट अलगाव स्तर (पंक्ति संस्करण) यहाँ सहायक हो सकता है?
मिकेल एरिकसन

शायद। या यह गतिरोध अपवादों को संगामिति अपवादों में बदल सकता है।
RBarryYoung

2
TABLOCK संकेत को उस तालिका पर निर्दिष्ट करना जो INSERT कथन का लक्ष्य है, TABLOCKX संकेत को निर्दिष्ट करने के समान प्रभाव है। (स्रोत: msdn.microsoft.com/en-us/library/bb510625.aspx )
tuespetre

31

यदि टेबल वेरिएबल केवल कभी एक मूल्य रखता है तो कोई समस्या नहीं होगी। कई पंक्तियों के साथ, गतिरोध के लिए एक नई संभावना है। मान लीजिए कि दो समवर्ती प्रक्रियाएँ (A & B) एक ही कंपनी के लिए (1, 2) और (2, 1) वाले तालिका चरों के साथ चलती हैं।

प्रक्रिया A गंतव्य को पढ़ता है, कोई पंक्ति नहीं पाता है, और '1' मान सम्मिलित करता है। यह मूल्य '1' पर एक विशेष पंक्ति लॉक रखता है। प्रोसेस बी गंतव्य को पढ़ता है, कोई पंक्ति नहीं पाता है, और '2' मान सम्मिलित करता है। यह '2' मूल्य पर एक विशेष पंक्ति लॉक रखता है।

अब प्रक्रिया A को पंक्ति 2 को संसाधित करने की आवश्यकता है, और B को पंक्ति 1 को संसाधित करने की आवश्यकता है। न तो प्रक्रिया प्रगति कर सकती है क्योंकि इसके लिए एक लॉक की आवश्यकता होती है जो अन्य प्रक्रिया द्वारा रखे गए अनन्य लॉक के साथ असंगत है।

कई पंक्तियों के साथ गतिरोध से बचने के लिए, पंक्तियों को हर बार एक ही क्रम में संसाधित (और एक्सेस किए गए टेबल) की आवश्यकता होती है । प्रश्न में दिखाए गए निष्पादन योजना में तालिका चर एक ढेर है, इसलिए पंक्तियों में कोई आंतरिक आदेश नहीं है (वे प्रविष्टि क्रम में पढ़ने की काफी संभावना है, हालांकि यह गारंटी नहीं है):

मौजूदा योजना

सुसंगत पंक्ति प्रसंस्करण आदेश की कमी सीधे गतिरोध के अवसर की ओर ले जाती है। एक दूसरा विचार यह है कि एक महत्वपूर्ण विशिष्टता की कमी का मतलब है कि हेलोवीन सुरक्षा प्रदान करने के लिए एक टेबल स्पूल आवश्यक है। स्पूल एक उत्सुक स्पूल है, जिसका अर्थ है कि सभी पंक्तियों को एक टेम्पर्ड बी लिखा जाता है वापस पढ़ने से पहले वर्कटेबिल में और इन्सर्ट ऑपरेटर के लिए रिप्ले किया जाता है।

TYPEएक क्लस्टर शामिल करने के लिए टेबल चर को पुनर्परिभाषित करना PRIMARY KEY:

DROP TYPE dbo.CoUserData;

CREATE TYPE dbo.CoUserData
AS TABLE
(
    MyKey   integer NOT NULL PRIMARY KEY CLUSTERED,
    MyValue integer NOT NULL
);

निष्पादन योजना अब क्लस्टर इंडेक्स की एक स्कैन दिखाती है और विशिष्टता की गारंटी का मतलब है कि अनुकूलक स्पेलर को सुरक्षित रूप से निकालने में सक्षम है:

प्राथमिक कुंजी के साथ

MERGE128 थ्रेड्स पर स्टेटमेंट के 5000 पुनरावृत्तियों के साथ परीक्षण में , क्लस्टर किए गए टेबल चर के साथ कोई गतिरोध नहीं हुआ। मुझे जोर देना चाहिए कि यह केवल अवलोकन के आधार पर है; संकुल सारणी चर ( तकनीकी रूप से ) विभिन्न प्रकार के आदेशों में अपनी पंक्तियों का उत्पादन कर सकता है , लेकिन एक सुसंगत आदेश की संभावना बहुत बढ़ जाती है। देखे गए व्यवहार को निश्चित रूप से हर नए संचयी अद्यतन, सर्विस पैक या SQL सर्वर के नए संस्करण के लिए पुनर्प्राप्त करने की आवश्यकता होगी।

यदि तालिका चर परिभाषा को बदला नहीं जा सकता है, तो एक और विकल्प है:

MERGE dbo.CompanyUser AS R
USING 
    (SELECT DISTINCT MyKey, MyValue FROM @DataTable) AS NewData ON
    R.CompanyId = @CompanyID
    AND R.UserID = @UserID
    AND R.MyKey = NewData.MyKey
WHEN NOT MATCHED THEN 
    INSERT 
        (CompanyID, UserID, MyKey, MyValue) 
    VALUES
        (@CompanyID, @UserID, NewData.MyKey, NewData.MyValue)
OPTION (ORDER GROUP);

यह स्पष्ट प्रकार की शुरुआत की लागत पर स्पूल (और पंक्ति-क्रम स्थिरता) को समाप्त करने में भी मदद करता है:

योजना को क्रमबद्ध करें

इस योजना ने भी उसी परीक्षण का उपयोग करके कोई गतिरोध उत्पन्न नहीं किया। नीचे प्रजनन स्क्रिप्ट:

CREATE TYPE dbo.CoUserData
AS TABLE
(
    MyKey   integer NOT NULL /* PRIMARY KEY */,
    MyValue integer NOT NULL
);
GO
CREATE TABLE dbo.Company
(
    CompanyID   integer NOT NULL

    CONSTRAINT PK_Company
        PRIMARY KEY (CompanyID)
);
GO
CREATE TABLE dbo.CompanyUser
(
    CompanyID   integer NOT NULL,
    UserID      integer NOT NULL,
    MyKey       integer NOT NULL,
    MyValue     integer NOT NULL

    CONSTRAINT PK_CompanyUser
        PRIMARY KEY CLUSTERED
            (CompanyID, UserID, MyKey),

    FOREIGN KEY (CompanyID)
        REFERENCES dbo.Company (CompanyID),
);
GO
CREATE NONCLUSTERED INDEX nc1
ON dbo.CompanyUser (CompanyID, UserID);
GO
INSERT dbo.Company (CompanyID) VALUES (1);
GO
DECLARE 
    @DataTable AS dbo.CoUserData,
    @CompanyID integer = 1,
    @UserID integer = 1;

INSERT @DataTable
SELECT TOP (10)
    V.MyKey,
    V.MyValue
FROM
(
    VALUES
        (1, 1),
        (2, 2),
        (3, 3),
        (4, 4),
        (5, 5),
        (6, 6),
        (7, 7),
        (8, 8),
        (9, 9)
) AS V (MyKey, MyValue)
ORDER BY NEWID();

BEGIN TRANSACTION;

    -- Test MERGE statement here

ROLLBACK TRANSACTION;

8

मुझे लगता है कि SQL_Kiwi ने बहुत अच्छा विश्लेषण प्रदान किया है। यदि आपको डेटाबेस में समस्या को हल करने की आवश्यकता है, तो आपको उसके सुझाव का पालन करना चाहिए। बेशक, आपको यह प्रतिशोध करने की आवश्यकता है कि यह आपके लिए हर बार आपके द्वारा काम करने, सर्विस पैक लागू करने, या इंडेक्स या इंडेक्स किए गए दृश्य को जोड़ने / बदलने के लिए काम करता है।

तीन अन्य विकल्प हैं:

  1. आप अपनी आवेषण को क्रमबद्ध कर सकते हैं ताकि वे टकराएं नहीं: आप अपने लेन-देन की शुरुआत में sp_getapplock को आमंत्रित कर सकते हैं और अपने MERGE को निष्पादित करने से पहले एक विशेष लॉक प्राप्त कर सकते हैं। बेशक आपको अभी भी इसका परीक्षण करने की आवश्यकता है।

  2. आपके पास अपने सभी आवेषणों को एक थ्रेड हैंडल हो सकता है, ताकि आपका ऐप सर्वर संगामिति को संभाल सके।

  3. गतिरोध के बाद आप स्वचालित रूप से पुन: प्रयास कर सकते हैं - यदि संगामिति अधिक है तो यह सबसे धीमा तरीका हो सकता है।

किसी भी तरह से, केवल आप प्रदर्शन पर अपने समाधान के प्रभाव को निर्धारित कर सकते हैं।

आमतौर पर हमारे सिस्टम में गतिरोध बिल्कुल नहीं होता है, हालांकि हमारे पास इनके होने की काफी संभावनाएं होती हैं। 2011 में हमने एक तैनाती में गलती की और कुछ ही घंटों में आधा दर्जन गतिरोध हो गए, सभी एक ही परिदृश्य का अनुसरण कर रहे थे। मैंने तय किया कि जल्द ही और उस वर्ष के लिए सभी गतिरोध थे।

हम ज्यादातर अपने सिस्टम में दृष्टिकोण 1 का उपयोग कर रहे हैं। यह हमारे लिए वास्तव में अच्छा काम करता है।


-1

एक अन्य संभावित दृष्टिकोण - मैंने मर्ज को कभी-कभी लॉकिंग और प्रदर्शन के मुद्दों को पेश करने के लिए पाया है - यह विकल्प (मैक्सडोप x) क्वेरी विकल्प के साथ खेलने के लायक हो सकता है

मंद और सुदूर अतीत में SQL सर्वर में इन्सर्ट रो लेवल लॉकिंग विकल्प था - लेकिन ऐसा लगता है कि एक मृत्यु हो गई है, हालांकि एक पहचान के साथ एक संकुल PK को क्लीन रन करना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.