2 টি সারণি থেকে অচলিত সমাধানটি কেবল ইনডেক্স ভিউয়ের মাধ্যমে সম্পর্কিত


17

আমার এমন পরিস্থিতি রয়েছে যেখানে আমি অচলাবস্থা পাচ্ছি এবং আমি মনে করি যে আমি অপরাধীদের সংকুচিত করেছি, তবে আমি এটি ঠিক করতে আমি কী করতে পারি তা আমি নিশ্চিত নই।

এটি এসকিউএল সার্ভার ২০০৮ আর 2 চালাচ্ছে এমন একটি পরিবেশ পরিবেশে।

আপনাকে পরিস্থিতি সম্পর্কে কিছুটা সরলীকৃত দর্শন দিতে:


নীচে সংজ্ঞায়িত হিসাবে আমার কাছে 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 (...)

এই 2 টি পদ্ধতি দুটিই রিড কমিটিকেটেড বিচ্ছিন্নতায় চালিত হয়। আমি 1222 বর্ধিত ইভেন্ট আউটপুট জিজ্ঞাসা করতে পরিচালিত করেছি, এবং ডেডলকগুলি সম্পর্কে নিম্নলিখিতটি ব্যাখ্যা করেছি:

এসপি 1 সূচকে RangeS-Sকী লকের জন্য অপেক্ষা করছে IX_follow_member_id_includesযখন এসপি 2 একটি বিবাদযুক্ত (এক্স) লক ধরে

এসপি 2 একটি Sমোড লকের জন্য অপেক্ষা করছে PK_member_activity যখন এসপি 1 একটি বিরোধী (এক্স) লক ধরে

অদলবদল প্রতিটি ক্যোয়ারির (অন্তর্ভুক্তি) শেষ লাইনে ঘটছে বলে মনে হচ্ছে। আমার কাছে যা অস্পষ্ট তা এই কারণেই এসপি 1 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

এছাড়াও, এসপি 1 এবং এসপি 2 কী করছে তার আরও সূক্ষ্ম চিত্র এখানে

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

এছাড়াও এসপি 2:

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

এবং এসপি 2 সহ:

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

এই দুটি পরিবর্তন সহ, আমি এখনও অচলাবস্থার মনে হচ্ছে।

যদি আমি সরবরাহ করতে পারি অন্য কিছু থাকে তবে দয়া করে আমাকে জানান। ধন্যবাদ।


পড়ার প্রতিশ্রুতিবদ্ধ কী-রেঞ্জের লক নেয় না, কেবল সিরিয়ালাইজযোগ্য করে। অচলাবস্থা যদি বাস্তবে প্রতিশ্রুতিবদ্ধ পড়তে দেখায় (২) তবে আমার অনুমানটি হ'ল আপনি কোনও বিদেশী কী পরিবর্তন করতে যাচ্ছেন যা কভারগুলির আওতায় সিরিয়ালাইজেটে পরিণত হবে (যদিও এখনও পড়তে প্রতিশ্রুতিবদ্ধ বলুন)। আমাদের আরও সতর্কতার সাথে সম্পূর্ণ ডিডিএল এবং এসপি প্রয়োজন।
শান গ্যালার্ডি

@ সানগ্যালার্ডি, ধন্যবাদ আমি ভুল ব্যাখ্যার ক্ষেত্রে 1222 আউটপুট অন্তর্ভুক্ত করার জন্য সম্পাদনা করেছি এবং এসপিরা কী করছে সে সম্পর্কে আমি আরও বিশদ যুক্ত করেছি। এটা কি সাহায্য করে?
লিল্যান্ড রিচার্ডসন

2
@ সানগ্যালার্ডি ক্যোয়ারী পরিকল্পনার যে অংশটি সূচীবদ্ধ দৃষ্টিভঙ্গি বজায় রেখেছে সেগুলি অভ্যন্তরীণভাবে চলছে SERIALIZABLE(এটির চেয়ে আরও কিছুটা বেশি, তবে এটি একটি উত্তর নয় :)
পল হোয়াইট 9

@ পালউইহাইট অন্তর্দৃষ্টিটির জন্য আপনাকে ধন্যবাদ, আমি এটি জানতাম না! দ্রুত পরীক্ষা করে, আমি অবশ্যই আপনার সঞ্চিত পদ্ধতিতে (রেঞ্জআই-এন, রেঞ্জএস-এস, রেঞ্জস-ইউ) সন্নিবেশকালে সূচকযুক্ত দৃশ্যের সাথে সিরিয়ালযোগ্যযোগ্য লকিং মোডগুলি পেতে পারি। দেখে মনে হচ্ছে যেন অচলাব লক মোডগুলি যখন আপনার লক সীমানার ভিতরে পড়ে তখন আপনার সঞ্চিত পদ্ধতিতে সন্নিবেশকালে একে অপরের বিরুদ্ধে যথাযথ সময়ে আঘাত করে ল্যাড মোডগুলি থেকে ঘটে চলেছে (উদাহরণস্বরূপ, রেঞ্জের লক ধরে থাকা অঞ্চলে)। একটি টাইমিং এবং একটি ইনপুট ডেটা সংঘর্ষ উভয়ই আমি ভাবি।
শান গ্যালার্ডি

প্রশ্ন: আমি যদি SELECT স্টেটমেন্টগুলিতে একটি হোল্ডলক ইঙ্গিতটি যুক্ত করি তবে তা কি লকটি onোকানো থেকে আটকাতে পারে?
লিল্যান্ড রিচার্ডসন

উত্তর:


5

দ্বন্দ্বটি সূচক network_activityদৃষ্টিভঙ্গিতে উত্সাহিত হয় যা ডিএমএল বিবৃতি জুড়ে (অভ্যন্তরীণভাবে) বজায় রাখা দরকার। সম্ভবত এসপি 1 IX_follow-member_id_includesসূচীতে একটি লক চাইছে কারণ এটি সম্ভবত ভিউ দ্বারা ব্যবহৃত হয়েছে (এটি ভিউটির জন্য একটি প্রচ্ছদ সূচক বলে মনে হচ্ছে)।

দুটি সম্ভাব্য বিকল্প:

  1. ক্লাস্টারড ইনডেক্সটি ভিউতে ফেলে দেওয়ার বিষয়টি বিবেচনা করুন যাতে এটি আর কোনও সূচিযুক্ত দৃশ্য না হয়। এটি থাকার সুবিধা কি রক্ষণাবেক্ষণ ব্যয়কে ছাড়িয়ে যায়? আপনি কি এটি থেকে প্রায়শই পর্যাপ্ত নির্বাচন করেন বা এটির মূল্য নির্ধারণের দ্বারা পারফরম্যান্স লাভ হয়? আপনি যদি এই প্রকগুলি বরং ঘন ঘন চালান, তবে সম্ভবত ব্যয়টি সুবিধার চেয়ে বেশি?

  2. যদি ভিউটিকে সূচকযুক্ত করার সুবিধাটি ব্যয় ছাড়িয়ে যায়, তবে সেই ভিউটির বেস টেবিলের বিপরীতে ডিএমএল অপারেশনগুলি বিচ্ছিন্ন করার বিষয়টি বিবেচনা করুন। এটি অ্যাপ্লিকেশন লকগুলির ব্যবহারের মাধ্যমে করা যায় (দেখুন sp_getapplock এবং sp_releaseapplock )। অ্যাপ্লিকেশন লকগুলি আপনাকে স্বেচ্ছাসেবী ধারণাগুলির চারপাশে লক তৈরি করতে দেয়। অর্থ, আপনি @Resourceআপনার উভয় সঞ্চিত প্রোকে "" নেটওয়ার্ক_অ্যাক্টিভিটি "হিসাবে সংজ্ঞায়িত করতে পারেন যা তাদের পালা অপেক্ষা করতে বাধ্য করবে। প্রতিটি প্রকল্প একই কাঠামো অনুসরণ করবে:

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

    আপনাকে ত্রুটিগুলি পরিচালনা করতে হবে / ROLLBACKনিজেকে (লিঙ্কযুক্ত এমএসডিএন ডকুমেন্টেশনে যেমন বলা হয়েছে) তাই সাধারণভাবে রাখুন TRY...CATCH। তবে, এটি আপনাকে পরিস্থিতি পরিচালনা করতে দেয়।
    দয়া করে নোট করুন: sp_getapplock / sp_releaseapplockখুব কম ব্যবহার করা উচিত; অ্যাপ্লিকেশন লকগুলি অবশ্যই খুব কার্যকর হতে পারে (যেমন এর মতো ক্ষেত্রে) তবে একেবারে প্রয়োজনীয় হলে সেগুলি ব্যবহার করা উচিত।


সাহায্যের জন্য ধন্যবাদ. আমি অপশন # 2 এ আরও কিছুটা পড়তে যাচ্ছি এবং দেখুন এটি আমাদের জন্য কার্যকর কিনা। ভিউটি বেশ খানিকটা থেকে পঠিত হয়েছে, এবং ক্লাস্টারড ইনডেক্স হ'ল বেশ বড় সাহায্য ... তাই আমি বরং এটিকে সরাতে পারতাম না। আমি একবার এই শট দেওয়ার পরে আমি একটি আপডেট ফিরে আসব।
লেল্যান্ড রিচার্ডসন

আমি মনে করি sp_getapplock ব্যবহার করে কাজ হবে। আমি এখনও আমাদের উত্পাদনের পরিবেশে এটি চেষ্টা করতে পারিনি, তবে আমি নিশ্চিত করতে চেয়েছিলাম যে এটির মেয়াদ শেষ হওয়ার আগেই আপনি এই অনুদানটি পেয়েছেন। আমি এখানে আপডেট করব যখন আমি এটির কাজটি নিশ্চিত করতে পারি!
লেল্যান্ড রিচার্ডসন

ধন্যবাদ। আবেদন লক্স প্রায় এক চমৎকার ব্যাপার হল আপনি ভালো কিছু concatenating এর গ্র্যানুলারিটি মাত্রা পরিবর্তন করতে পারেন member_idমধ্যে @Resourceমান। এটি এই নির্দিষ্ট পরিস্থিতির ক্ষেত্রে প্রযোজ্য বলে মনে হয় না তবে আমি এটি এটির মতো ব্যবহার করতে দেখেছি এবং এটি বেশ কার্যকর, বিশেষত একটি বহু-ভাড়াটে ব্যবস্থায় যেখানে আপনি প্রতিটি গ্রাহক ভিত্তিতে প্রক্রিয়াটি একক থ্রেডে সীমাবদ্ধ রাখতে চান, তবে এখনও এটি গ্রাহকদের জুড়ে মাল্টিথ্রেড করা আছে।
সলোমন রুটজকি

আমি একটি আপডেট দিতে চাই এবং বলে যে এই হয়নি আমাদের উত্পাদন পরিবেশে কাজ শেষ। :)
লিল্যান্ড রিচার্ডসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.