केवल अनुक्रमित दृश्य के माध्यम से संबंधित 2 तालिकाओं से गतिरोध को हल करना


17

मेरे पास एक ऐसी स्थिति है जहां मुझे गतिरोध मिल रहा है, और मुझे लगता है कि मैंने अपराधियों को कम कर दिया है, लेकिन मुझे यकीन नहीं है कि मैं इसे ठीक करने के लिए क्या कर सकता हूं।

यह SQL Server 2008 R2 चलाने वाले उत्पादन परिवेश पर है।

आपको स्थिति का थोड़ा सरलीकृत दृश्य देने के लिए:


मेरे पास नीचे बताई गई 3 तालिकाएँ हैं:

TABLE activity (
    id, -- PK
    ...
)

TABLE member_activity (
    member_id, -- PK col 1
    activity_id, -- PK col 2
    ...
)

TABLE follow (
    id, -- PK
    follower_id,
    member_id,
    ...
)

member_activityतालिका एक यौगिक प्राथमिक कुंजी के रूप में परिभाषित किया गया है member_id, activity_idक्योंकि मैं ही कभी उस तालिका कि रास्ते में डेटा को देखने की जरूरत है।

मेरे पास एक गैर-सूचीबद्ध सूचकांक भी है follow:

CREATE NONCLUSTERED INDEX [IX_follow_member_id_includes] 
ON follow ( member_id ASC ) INCLUDE ( follower_id )

इसके अतिरिक्त, मेरे पास स्कीमा-बाउंड व्यू है network_activityजिसे निम्नानुसार परिभाषित किया गया है:

CREATE VIEW network_activity
WITH SCHEMABINDING
AS

SELECT
    follow.follower_id as member_id,
    member_activity.activity_id as activity_id,
    COUNT_BIG(*) AS cb
FROM member_activity
INNER JOIN follow ON follow.member_id = member_activity.member_id
INNER JOIN activity ON activity.id = member_activity.activity_id
GROUP BY follow.follower_id, member_activity.activity_id

जिसमें एक अद्वितीय क्लस्टर इंडेक्स भी है:

CREATE UNIQUE CLUSTERED INDEX [IX_network_activity_unique_member_id_activity_id] 
ON network_activity
(
    member_id ASC,
    activity_id ASC
)

अब, मेरे पास दो गतिरोध संग्रहीत कार्यविधियाँ हैं। वे निम्नलिखित प्रक्रिया से गुजरते हैं:

-- SP1: insert activity
-----------------------
INSERT INTO activity (...)
SELECT ... FROM member_activity WHERE member_id = @a AND activity_id = @b
INSERT INTO member_activity (...)


-- SP2: insert follow
---------------------
SELECT follow WHERE member_id = @x AND follower_id = @y
INSERT INTO follow (...)

READ COMMITTED अलगाव में ये 2 प्रक्रियाएं चलती हैं। मैं 1222 विस्तारित ईवेंट आउटपुट को क्वेरी करने में कामयाब रहा, और गतिरोध के संबंध में निम्नलिखित व्याख्या की है:

SP1 इंडेक्स RangeS-Sपर एक कुंजी लॉक की प्रतीक्षा कर रहा है IX_follow_member_id_includesजबकि SP2 एक परस्पर विरोधी (X) लॉक रखता है

SP2 एक Sमोड लॉक की प्रतीक्षा कर रहा है, PK_member_activity जबकि SP1 में एक परस्पर विरोधी (X) लॉक होता है

गतिरोध प्रत्येक क्वेरी (आवेषण) की अंतिम पंक्ति पर दिखाई देता है। मेरे लिए क्या अस्पष्ट है क्यों SP1 IX_follow-member_id_includesसूचकांक पर एक ताला चाहता है । मेरे लिए एकमात्र लिंक, इस अनुक्रमित दृश्य से प्रतीत होता है, यही वजह है कि मैंने इसे शामिल किया है।

इन गतिरोधों को होने से रोकने के लिए मेरे लिए सबसे अच्छा तरीका क्या होगा? कोई भी सहायताकाफी प्रशंसनीय होगी। गतिरोध के मुद्दों को हल करने में मुझे ज्यादा अनुभव नहीं है।

कृपया मुझे बताएं कि क्या कोई और जानकारी है जो मैं प्रदान कर सकता हूं जो मदद कर सकता है!

अग्रिम में धन्यवाद।


संपादन 1: अनुरोध के अनुसार कुछ और जानकारी जोड़ना।

यहाँ इस गतिरोध से 1222 उत्पादन है:

<deadlock>
    <victim-list>
        <victimProcess id="process4c6672748" />
    </victim-list>
    <process-list>
        <process id="process4c6672748" taskpriority="0" logused="332" waitresource="KEY: 8:72057594104905728 (25014f77eaba)" waittime="581" ownerId="474698706" transactionname="INSERT" lasttranstarted="2014-07-03T17:03:12.287" XDES="0x298487970" lockMode="RangeS-S" schedulerid="1" kpid="972" status="suspended" spid="79" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-07-03T17:03:12.283" lastbatchcompleted="2014-07-03T17:03:12.283" lastattention="2014-07-03T10:25:00.283" clientapp=".Net SqlClient Data Provider" hostname="WIN08CLYDESDALE" hostpid="4596" loginname="TechPro" isolationlevel="read committed (2)" xactid="474698706" currentdb="8" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
            <executionStack>
                <frame procname="" line="7" stmtstart="1194" stmtend="1434" sqlhandle="0x02000000a26bb72a2b220406876cad09c22242e5265c82e6" />
                <frame procname="" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000" />
            </executionStack>
            <inputbuf> <!-- SP 1 --> </inputbuf>
        </process>
        <process id="process6cddc5b88" taskpriority="0" logused="456" waitresource="KEY: 8:72057594098679808 (89013169fc76)" waittime="567" ownerId="474698698" transactionname="INSERT" lasttranstarted="2014-07-03T17:03:12.283" XDES="0x30c459970" lockMode="S" schedulerid="4" kpid="4204" status="suspended" spid="70" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-07-03T17:03:12.283" lastbatchcompleted="2014-07-03T17:03:12.283" lastattention="2014-07-03T15:04:55.870" clientapp=".Net SqlClient Data Provider" hostname="WIN08CLYDESDALE" hostpid="4596" loginname="TechPro" isolationlevel="read committed (2)" xactid="474698698" currentdb="8" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
            <executionStack>
                <frame procname="" line="18" stmtstart="942" stmtend="1250" sqlhandle="0x03000800ca458d315ee9130100a300000100000000000000" />
            </executionStack>
            <inputbuf> <!-- SP 2 --> </inputbuf>
        </process>
    </process-list>
    <resource-list>
        <keylock hobtid="72057594104905728" dbid="8" objectname="" indexname="" id="lock33299fc00" mode="X" associatedObjectId="72057594104905728">
            <owner-list>
                <owner id="process6cddc5b88" mode="X" />
            </owner-list>
            <waiter-list>
                <waiter id="process4c6672748" mode="RangeS-S" requestType="wait" />
            </waiter-list>
        </keylock>
        <keylock hobtid="72057594098679808" dbid="8" objectname="" indexname="" id="lockb7e2ba80" mode="X" associatedObjectId="72057594098679808">
            <owner-list>
                <owner id="process4c6672748" mode="X" />
            </owner-list>
            <waiter-list>
                <waiter id="process6cddc5b88" mode="S" requestType="wait" />
            </waiter-list>
        </keylock>
    </resource-list>
</deadlock>

इस मामले में,

संबद्धऑब्जेक्टआई 72057594098679808 से मेल खाती है member_activity, PK_member_activity

संबद्धऑब्जेक्टआई 72057594104905728 से मेल खाती है follow, IX_follow_member_id_includes

इसके अलावा, यहाँ SP1 और SP2 क्या कर रहे हैं, एक अधिक सटीक तस्वीर है

-- SP1: insert activity
-----------------------
DECLARE @activityId INT

INSERT INTO activity (field1, field2)
VALUES (@field1, @field2)

SET @activityId = SCOPE_IDENTITY();

IF NOT EXISTS(
    SELECT TOP 1 member_id 
    FROM member_activity 
    WHERE member_id = @m1 AND activity_id = @activityId
)
    INSERT INTO member_activity (member_id, activity_id, field1)
    VALUES (@m1, @activityId, @field1)

IF NOT EXISTS(
    SELECT TOP 1 member_id 
    FROM member_activity 
    WHERE member_id = @m2 AND activity_id = @activityId
)
    INSERT INTO member_activity (member_id, activity_id, field1)
    VALUES (@m2, @activityId, @field1)

SP2 भी:

-- SP2: insert follow
---------------------

IF NOT EXISTS(
    SELECT TOP 1 1 
    FROM follow
    WHERE member_id = @memberId AND follower_id = @followerId
)
    INSERT INTO follow (member_id, follower_id)
    VALUES (@memberId, @followerId)

संपादित करें 2: टिप्पणियों को पुन: प्रसारित करने के बाद, मैंने सोचा कि मैं कुछ जानकारी जोड़ना चाहूंगा कि विदेशी कुंजी क्या हैं ...

  • member_activity.member_idएक memberमेज के लिए एक विदेशी कुंजी है
  • member_activity.activity_idactivityतालिका की एक विदेशी कुंजी है
  • follow.member_idएक memberमेज के लिए एक विदेशी कुंजी है
  • follow.follower_idएक memberमेज के लिए एक विदेशी कुंजी है

अपडेट 1:

मैंने कुछ बदलाव किए, जिनके बारे में मुझे लगा कि बिना किसी भाग्य के गतिरोध को रोकने में मदद मिल सकती है।

मेरे द्वारा किए गए परिवर्तन इस प्रकार थे:

-- SP1: insert activity
-----------------------
DECLARE @activityId INT

INSERT INTO activity (field1, field2)
VALUES (@field1, @field2)

SET @activityId = SCOPE_IDENTITY();

MERGE member_activity WITH ( HOLDLOCK ) as target
USING (SELECT @m1 as member_id, @activityId as activity_id, @field1 as field1) as source
    ON target.member_id = source.member_id
    AND target.activity_id = source.activity_id
WHEN NOT MATCHED THEN
    INSERT (member_id, activity_id, field1)
    VALUES (source.member_id, source.activity_id, source.field1)
;

MERGE member_activity WITH ( HOLDLOCK ) as target
USING (SELECT @m2 as member_id, @activityId as activity_id, @field1 as field1) as source
    ON target.member_id = source.member_id
    AND target.activity_id = source.activity_id
WHEN NOT MATCHED THEN
    INSERT (member_id, activity_id, field1)
    VALUES (source.member_id, source.activity_id, source.field1)
;

और SP2 के साथ:

-- SP2: insert follow
---------------------

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION

IF NOT EXISTS(
    SELECT TOP 1 1 
    FROM follow WITH ( UPDLOCK )
    WHERE member_id = @memberId AND follower_id = @followerId
)
    INSERT INTO follow (member_id, follower_id)
    VALUES (@memberId, @followerId)

COMMIT

इन दो परिवर्तनों के साथ, मुझे अभी भी गतिरोध हो रहे हैं।

अगर कुछ और है जो मैं प्रदान कर सकता हूं, तो कृपया मुझे बताएं। धन्यवाद।


पढ़े जाने वाले कुंजी-श्रेणी के ताले नहीं लेते हैं, केवल सीरियल करने योग्य है। अगर गतिरोध वास्तव में कमिटेड शो शो करता है (2) तो मेरे गेस आप एक विदेशी कुंजी को बदलने के लिए पहुंच रहे हैं जिसे कवर के तहत धारावाहिक में बदल दिया जाएगा (हालांकि अभी भी कहा गया है कि पढ़े हुए हैं)। हमें ईमानदारी से पूरे ddl और sp की जरूरत है ताकि आगे मदद कर सकें।
शॉन का कहना है कि सारा चिप्स

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

2
@SeanGallardy क्वेरी योजना है कि अनुक्रमित दृश्य का कहना है के भाग में आंतरिक रूप से चलाता है SERIALIZABLE(उस की तुलना में यह करने के लिए एक सा अधिक है, लेकिन यह एक टिप्पणी नहीं एक जवाब :) है
पॉल व्हाइट 9

@PaulWhite अंतर्दृष्टि के लिए धन्यवाद, मुझे नहीं पता था कि! एक त्वरित परीक्षण करते हुए, मैं निश्चित रूप से आपके संग्रहीत कार्यविधियों (रेंजआई-एन, रेंजएस-एस, रेंजएस-यू) में डालने के दौरान अनुक्रमित दृश्य के साथ क्रमिक लॉकिंग मोड प्राप्त कर सकता हूं। ऐसा लगता है कि गतिरोध असंगत लॉक मोड्स से हो रहा है, जो आपकी संग्रहीत प्रक्रियाओं में आवेषण के दौरान सही समय पर एक-दूसरे से टकराते हैं, जब वे लॉक बाउंड्रीज़ के अंदर आते हैं (उदाहरण के लिए रेंज लॉक द्वारा रखे गए क्षेत्र में)। समय और इनपुट डेटा टकराव दोनों मुझे लगता है।
शॉन का कहना है कि सारा चिप्स

प्रश्न: अगर मैंने SELECT स्टेटमेंट पर एक HOLDLOCK हिंट जोड़ा, तो क्या लॉक को इंसर्ट पर होने से रोका जा सकेगा?
लंडन रिचर्डसन

जवाबों:


5

संघर्ष network_activityएक अनुक्रमित दृश्य होने के लिए उबलता है जिसे डीएमएल बयानों के दौरान (आंतरिक रूप से) बनाए रखने की आवश्यकता होती है। यह सबसे अधिक संभावना है कि SP1 IX_follow-member_id_includesइंडेक्स पर लॉक क्यों चाह रहा है क्योंकि यह संभवतः व्यू द्वारा उपयोग किया जाता है (यह व्यू के लिए एक कवरिंग इंडेक्स लगता है)।

दो संभावित विकल्प:

  1. View पर Clustered Index को छोड़ने पर विचार करें ताकि यह अब Indexed View न हो। क्या इसका रखरखाव लागत से आगे बढ़ना लाभ है? क्या आप इसे अक्सर पर्याप्त रूप से चुनते हैं या क्या इसके प्रदर्शन का मूल्य इसके लायक है? यदि आप इन प्रॉपर को बार-बार चलाते हैं, तो हो सकता है कि लागत लाभ से अधिक हो?

  2. यदि दृश्य को अनुक्रमित किए जाने का लाभ लागत को कम करता है, तो उस दृश्य के आधार तालिकाओं के विरुद्ध डीएमएल संचालन को अलग करने पर विचार करें। यह एप्लिकेशन लॉक के उपयोग के माध्यम से किया जा सकता है (देखें sp_getapplock और sp_releaseapplock )। एप्लिकेशन लॉक आपको मनमानी अवधारणाओं के आसपास ताले बनाने देता है। मतलब, आप @Resourceअपने Stored Procs दोनों में "network_activity" के रूप में परिभाषित कर सकते हैं जो उन्हें अपनी बारी का इंतजार करने के लिए मजबूर करेगा। प्रत्येक खरीद एक ही संरचना का पालन करेगी:

    BEGIN TRANSACTION;
    EXEC sp_getapplock @Resource = 'network_activity', @LockMode = 'Exclusive';
    ...current proc code...
    EXEC sp_releaseapplock @Resource = 'network_activity';
    COMMIT TRANSACTION;

    आपको त्रुटियों / ROLLBACKस्वयं को प्रबंधित करने की आवश्यकता है (जैसा कि लिंक किए गए MSDN प्रलेखन में कहा गया है) इसलिए हमेशा की तरह TRY...CATCH। लेकिन, यह आपको स्थिति का प्रबंधन करने की अनुमति देता है।
    कृपया ध्यान दें: sp_getapplock / sp_releaseapplockसंयम से इस्तेमाल किया जाना चाहिए; एप्लिकेशन लॉक निश्चित रूप से बहुत उपयोगी हो सकते हैं (जैसे कि इस तरह के मामलों में) लेकिन उनका उपयोग केवल तभी किया जाना चाहिए जब बिल्कुल आवश्यक हो।


सहायता के लिए धन्यवाद। मैं विकल्प # 2 पर थोड़ा और पढ़ने जा रहा हूं और देखें कि क्या यह हमारे लिए काम करता है। यह दृश्य काफी हद तक पढ़ा जाता है, और क्लस्टर इंडेक्स एक बहुत बड़ी मदद है ... इसलिए मैं इसे अभी तक नहीं हटाऊंगा। एक बार शॉट देने के बाद मैं एक अपडेट वापस आऊंगा।
लीलैंड रिचर्डसन

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

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

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