এসকিউএল সার্ভারে ডেডলক ছাড়াই কোনও কী টেবিলে একযোগে অ্যাক্সেস পরিচালনা করা


32

আমার কাছে একটি টেবিল রয়েছে যা উত্তরাধিকারের অ্যাপ্লিকেশন দ্বারা IDENTITYঅন্যান্য বিভিন্ন সারণীতে ক্ষেত্রের বিকল্প হিসাবে ব্যবহৃত হয় ।

টেবিলের প্রতিটি সারি LastIDক্ষেত্রের জন্য ব্যবহৃত সর্বশেষ ব্যবহৃত আইডি সঞ্চয় করে IDName

মাঝেমধ্যে সঞ্চিত সংগ্রহটি একটি অচলাবস্থা পায় - আমি বিশ্বাস করি যে আমি একটি উপযুক্ত ত্রুটি হ্যান্ডলার তৈরি করেছি; তবে আমি আগ্রহী যে এই পদ্ধতিটি আমার যেমন মনে হয় ঠিক তেমন কাজ করে কিনা, বা আমি যদি এখানে ভুল গাছটি ছাঁটাই করছি।

আমি মোটামুটি নিশ্চিত যে কোনও টেড লক ছাড়াই এই টেবিলটি অ্যাক্সেস করার কোনও উপায় থাকতে হবে।

ডাটাবেস নিজেই সাথে কনফিগার করা হয় READ_COMMITTED_SNAPSHOT = 1

প্রথমত, এখানে টেবিলটি রয়েছে:

CREATE TABLE [dbo].[tblIDs](
    [IDListID] [int] NOT NULL 
        CONSTRAINT PK_tblIDs 
        PRIMARY KEY CLUSTERED 
        IDENTITY(1,1) ,
    [IDName] [nvarchar](255) NULL,
    [LastID] [int] NULL,
);

এবং IDNameমাঠে অবিচ্ছিন্ন সূচক :

CREATE NONCLUSTERED INDEX [IX_tblIDs_IDName] 
ON [dbo].[tblIDs]
(
    [IDName] ASC
) 
WITH (
    PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON
    , FILLFACTOR = 80
);

GO

কিছু নমুনা তথ্য:

INSERT INTO tblIDs (IDName, LastID) 
    VALUES ('SomeTestID', 1);
INSERT INTO tblIDs (IDName, LastID) 
    VALUES ('SomeOtherTestID', 1);
GO

সারণীতে সঞ্চিত মানগুলি আপডেট করার জন্য ব্যবহৃত সঞ্চিত পদ্ধতিটি এবং পরবর্তী আইডিটি ফেরত দিন:

CREATE PROCEDURE [dbo].[GetNextID](
    @IDName nvarchar(255)
)
AS
BEGIN
    /*
        Description:    Increments and returns the LastID value from tblIDs
        for a given IDName
        Author:         Max Vernon
        Date:           2012-07-19
    */

    DECLARE @Retry int;
    DECLARE @EN int, @ES int, @ET int;
    SET @Retry = 5;
    DECLARE @NewID int;
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET NOCOUNT ON;
    WHILE @Retry > 0
    BEGIN
        BEGIN TRY
            BEGIN TRANSACTION;
            SET @NewID = COALESCE((SELECT LastID 
                FROM tblIDs 
                WHERE IDName = @IDName),0)+1;
            IF (SELECT COUNT(IDName) 
                FROM tblIDs 
                WHERE IDName = @IDName) = 0 
                    INSERT INTO tblIDs (IDName, LastID) 
                    VALUES (@IDName, @NewID)
            ELSE
                UPDATE tblIDs 
                SET LastID = @NewID 
                WHERE IDName = @IDName;
            COMMIT TRANSACTION;
            SET @Retry = -2; /* no need to retry since the operation completed */
        END TRY
        BEGIN CATCH
            IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
                SET @Retry = @Retry - 1;
            ELSE
                BEGIN
                SET @Retry = -1;
                SET @EN = ERROR_NUMBER();
                SET @ES = ERROR_SEVERITY();
                SET @ET = ERROR_STATE()
                RAISERROR (@EN,@ES,@ET);
                END
            ROLLBACK TRANSACTION;
        END CATCH
    END
    IF @Retry = 0 /* must have deadlock'd 5 times. */
    BEGIN
        SET @EN = 1205;
        SET @ES = 13;
        SET @ET = 1
        RAISERROR (@EN,@ES,@ET);
    END
    ELSE
        SELECT @NewID AS NewID;
END
GO

সঞ্চিত প্রকল্পের নমুনা সম্পাদন:

EXEC GetNextID 'SomeTestID';

NewID
2

EXEC GetNextID 'SomeTestID';

NewID
3

EXEC GetNextID 'SomeOtherTestID';

NewID
2

সম্পাদনা করুন:

আমি একটি নতুন সূচক যুক্ত করেছি, যেহেতু বিদ্যমান সূচক IX_tblIDs_Name এসপি ব্যবহার করছে না; আমি ধরে নিই যে ক্যোরি প্রসেসর ক্লাস্টারড ইনডেক্স ব্যবহার করছে কারণ এটি লাস্টআইডি-তে সঞ্চিত মান প্রয়োজন। যাইহোক, এই সূচকটি প্রকৃত বাস্তবায়ন পরিকল্পনার দ্বারা ব্যবহৃত হয়:

CREATE NONCLUSTERED INDEX IX_tblIDs_IDName_LastID 
ON dbo.tblIDs
(
    IDName ASC
) 
INCLUDE
(
    LastID
)
WITH (FILLFACTOR = 100
    , ONLINE=ON
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON);

সম্পাদনা # 2:

@ অ্যারোনবার্ট্র্যান্ড যে পরামর্শ দিয়েছিল এবং তাতে কিছুটা সংশোধন করেছে আমি তা গ্রহণ করেছি। এখানে সাধারণ ধারণাটি অপ্রয়োজনীয় লকিং অপসারণের জন্য এবং সামগ্রিকভাবে এসপিকে আরও দক্ষ করে তোলার জন্য বিবৃতিটি পরিমার্জন করা।

কোড নিচে থেকে উপরের কোড প্রতিস্থাপন BEGIN TRANSACTIONকরতে END TRANSACTION:

BEGIN TRANSACTION;
SET @NewID = COALESCE((SELECT LastID 
        FROM dbo.tblIDs 
        WHERE IDName = @IDName), 0) + 1;

IF @NewID = 1
    INSERT INTO tblIDs (IDName, LastID) 
    VALUES (@IDName, @NewID);
ELSE
    UPDATE dbo.tblIDs 
    SET LastID = @NewID 
    WHERE IDName = @IDName;

COMMIT TRANSACTION;

যেহেতু আমাদের কোডটি 0 টি দিয়ে এই টেবিলটিতে কখনই কোনও রেকর্ড যুক্ত করে না LastIDআমরা এই ধারণাটি তৈরি করতে পারি যে @ নিউইডআইডি যদি 1 হয় তবে সেই উদ্দেশ্যটি তালিকায় একটি নতুন আইডি যুক্ত করা হয়, অন্যথায় আমরা তালিকার একটি বিদ্যমান সারি আপডেট করছি।


আরসিএসআই সমর্থন করার জন্য আপনি কীভাবে ডাটাবেসটি কনফিগার করেছেন তা অপ্রাসঙ্গিক। আপনি ইচ্ছাকৃতভাবে SERIALIZABLEএখানে চলেছেন ।
অ্যারন বারট্র্যান্ড

হ্যাঁ, আমি কেবল সমস্ত প্রাসঙ্গিক তথ্য যুক্ত করতে চেয়েছিলাম। আপনি আনন্দিত যে আপনি এটি অপ্রাসঙ্গিক নিশ্চিত করেছেন!
ম্যাক্স ভার্নন

স্প_গেট্যাপলক কোনও অচলাবস্থার শিকার হওয়া খুব সহজ, তবে আপনি যদি লেনদেন শুরু করেন না তবে এক্সক্লুসিভ লকটি অর্জন করার জন্য একবার sp_getapplock কল করুন এবং আপনার পরিবর্তনটি নিয়ে এগিয়ে যান।
একে

1
আইডি নামটি কি অনন্য? তারপরে সুপারিশ করুন " অনন্য অবিচ্ছিন্ন সূচক তৈরি করুন "। তবে যদি আপনার নাল মান প্রয়োজন হয় তবে সূচকটিও ফিল্টার করা দরকার ।
ক্রোকুসেক

উত্তর:


15

প্রথমত, আমি প্রতিটি মানের জন্য ডাটাবেসে একটি বৃত্তাকার ট্রিপ করা এড়াতে চাই। উদাহরণস্বরূপ, যদি আপনার অ্যাপ্লিকেশনটি জানতে পারে এটির জন্য 20 টি নতুন আইডি প্রয়োজন, 20 রাউন্ড ট্রিপ করবেন না। কেবলমাত্র একটি সঞ্চিত পদ্ধতি কল করুন এবং ২০ টির চেয়ে বাড়িয়ে দিন your

পুরোপুরি ডেডলকগুলি এড়ানো সম্ভব। আমার সিস্টেমে আমার কোনও ডেডলক নেই। এটি সম্পাদন করার বিভিন্ন উপায় রয়েছে। আমি কীভাবে ডেডলকগুলি মুছে ফেলার জন্য sp_getapplock ব্যবহার করব তা দেখাব। এটি আপনার পক্ষে কাজ করবে কিনা আমার কোনও ধারণা নেই, কারণ এসকিউএল সার্ভারটি বন্ধ উত্স, সুতরাং আমি উত্স কোডটি দেখতে পাচ্ছি না এবং যেমন আমি জানি না যে আমি সমস্ত সম্ভাব্য কেস পরীক্ষা করেছি কিনা।

নিম্নলিখিতটি আমার জন্য কী কাজ করে তা বর্ণনা করে। YMMV।

প্রথমে আসুন আমরা এমন একটি দৃশ্য দিয়ে শুরু করি যেখানে আমরা সর্বদা যথেষ্ট পরিমাণে অচলাবস্থা পাই। দ্বিতীয়ত, আমরা এগুলি দূর করতে sp_getapplock ব্যবহার করব। এখানে সর্বাধিক গুরুত্বপূর্ণ বিষয় হ'ল চাপটি আপনার সমাধানটি পরীক্ষা করা। আপনার সমাধানটি ভিন্ন হতে পারে, তবে আপনাকে এটিকে উচ্চ সম্মতিতে প্রকাশ করা দরকার, কারণ আমি পরে তা প্রদর্শন করব।

পূর্বশর্ত

আসুন কিছু টেস্ট ডেটা সহ একটি টেবিল সেট আপ করুন:

CREATE TABLE dbo.Numbers(n INT NOT NULL PRIMARY KEY); 
GO 

INSERT INTO dbo.Numbers 
    ( n ) 
        VALUES  ( 1 ); 
GO 
DECLARE @i INT; 
    SET @i=0; 
WHILE @i<21  
    BEGIN 
    INSERT INTO dbo.Numbers 
        ( n ) 
        SELECT n + POWER(2, @i) 
        FROM dbo.Numbers; 
    SET @i = @i + 1; 
    END;  
GO

SELECT n AS ID, n AS Key1, n AS Key2, 0 AS Counter1, 0 AS Counter2
INTO dbo.DeadlockTest FROM dbo.Numbers
GO

ALTER TABLE dbo.DeadlockTest ADD CONSTRAINT PK_DeadlockTest PRIMARY KEY(ID);
GO

CREATE INDEX DeadlockTestKey1 ON dbo.DeadlockTest(Key1);
GO

CREATE INDEX DeadlockTestKey2 ON dbo.DeadlockTest(Key2);
GO

নিম্নলিখিত দুটি পদ্ধতি অচলাবস্থায় জড়িয়ে পড়ার যথেষ্ট সম্ভাবনা রয়েছে:

CREATE PROCEDURE dbo.UpdateCounter1 @Key1 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
SET @Key1=@Key1-10000;
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
COMMIT;
GO

CREATE PROCEDURE dbo.UpdateCounter2 @Key2 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
SET @Key2=@Key2-10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
SET @Key2=@Key2+10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
COMMIT;
GO

ডেডলকগুলি পুনরুত্পাদন করছে

নীচের লুপগুলি প্রতিবার চালানোর সময় 20 টিরও বেশি ডেডলকের পুনরুত্পাদন করা উচিত। যদি আপনি 20 এরও কম পান তবে পুনরাবৃত্তির সংখ্যা বাড়ান।

একটি ট্যাবে, এটি চালান;

DECLARE @i INT, @DeadlockCount INT;
SELECT @i=0, @DeadlockCount=0;

WHILE @i<5000 BEGIN ;
  BEGIN TRY 
    EXEC dbo.UpdateCounter1 @Key1=123456;
  END TRY
  BEGIN CATCH
    SET @DeadlockCount = @DeadlockCount + 1;
    ROLLBACK;
  END CATCH ;
  SET @i = @i + 1;
END;
SELECT 'Deadlocks caught: ', @DeadlockCount ;

অন্য ট্যাবে, এই স্ক্রিপ্টটি চালান।

DECLARE @i INT, @DeadlockCount INT;
SELECT @i=0, @DeadlockCount=0;

WHILE @i<5000 BEGIN ;
  BEGIN TRY 
    EXEC dbo.UpdateCounter2 @Key2=123456;
  END TRY
  BEGIN CATCH
    SET @DeadlockCount = @DeadlockCount + 1;
    ROLLBACK;
  END CATCH ;
  SET @i = @i + 1;
END;
SELECT 'Deadlocks caught: ', @DeadlockCount ;

আপনি উভয়ই কয়েক সেকেন্ডের মধ্যে শুরু করে তা নিশ্চিত করুন।

ডেডলকগুলি মুছে ফেলার জন্য sp_getapplock ব্যবহার করা

উভয় পদ্ধতি পরিবর্তন করুন, লুপটি পুনরায় চালু করুন এবং দেখুন যে আপনার আর ডেডলক নেই:

ALTER PROCEDURE dbo.UpdateCounter1 @Key1 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
EXEC sp_getapplock @Resource='DeadlockTest', @LockMode='Exclusive';
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
SET @Key1=@Key1-10000;
UPDATE dbo.DeadlockTest SET Counter1=Counter1+1 WHERE Key1=@Key1;
COMMIT;
GO

ALTER PROCEDURE dbo.UpdateCounter2 @Key2 INT
AS
SET NOCOUNT ON ;
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION ;
EXEC sp_getapplock @Resource='DeadlockTest', @LockMode='Exclusive';
SET @Key2=@Key2-10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
SET @Key2=@Key2+10000;
UPDATE dbo.DeadlockTest SET Counter2=Counter2+1 WHERE Key2=@Key2;
COMMIT;
GO

ডেডলকগুলি দূর করতে এক সারি সহ একটি টেবিল ব্যবহার করা

Sp_getapplock শুরু করার পরিবর্তে, আমরা নিম্নলিখিত টেবিলটি সংশোধন করতে পারি:

CREATE TABLE dbo.DeadlockTestMutex(
ID INT NOT NULL,
CONSTRAINT PK_DeadlockTestMutex PRIMARY KEY(ID),
Toggle INT NOT NULL);
GO

INSERT INTO dbo.DeadlockTestMutex(ID, Toggle)
VALUES(1,0);

একবার আমরা এই টেবিলটি তৈরি এবং জনবহুল হয়ে গেলে, আমরা নিম্নলিখিত লাইনটি প্রতিস্থাপন করতে পারি

EXEC sp_getapplock @Resource='DeadlockTest', @LockMode='Exclusive';

উভয় পদ্ধতিতে এটির সাথে:

UPDATE dbo.DeadlockTestMutex SET Toggle = 1 - Toggle WHERE ID = 1;

আপনি স্ট্রেস টেস্টটি পুনরায় চালু করতে পারেন এবং নিজের জন্য দেখুন যে আমাদের কোনও অচল লক নেই।

উপসংহার

যেমনটি আমরা দেখেছি, sp_getapplock অন্যান্য সংস্থানগুলিতে অ্যাক্সেসকে সিরিয়ালাইজ করতে ব্যবহার করা যেতে পারে। যেমন এটি অচলাবস্থা দূর করতে ব্যবহার করা যেতে পারে।

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

এই পদ্ধতির ব্যবহার করার আগে আপনার নিজের এটি পরীক্ষা করার জন্য চাপ দেওয়া দরকার। প্রথমত, আপনাকে অবশ্যই নিশ্চিত করতে হবে যে আপনি আপনার আসল পদ্ধতির সাথে কমপক্ষে কয়েক ডজন ডেডলক পেয়েছেন। দ্বিতীয়ত, আপনি সংশোধিত সঞ্চিত পদ্ধতি ব্যবহার করে একই রেপ্রো স্ক্রিপ্টটি পুনরায় চালু করার সময় আপনার কোনও অচলাবস্থা নেই।

সাধারণভাবে, আমি মনে করি না যে আপনার টি-এসকিউএলটি ডেডলকগুলি থেকে কেবল এটির দ্বারা দেখানো বা সম্পাদনের পরিকল্পনা দেখে নিরাপদ কিনা তা নির্ধারণ করার কোনও ভাল উপায় আছে। আপনার কোড ডেডলকস প্রবণ কিনা তা নির্ধারণ করার একমাত্র উপায় আইএমও হ'ল উচ্চ সম্মতিতে প্রকাশ করা।

অচলাবস্থা দূর করার জন্য শুভকামনা! আমাদের সিস্টেমে মোটেও কোনও ডেডলক নেই, যা আমাদের কর্ম-জীবন ভারসাম্যের জন্য দুর্দান্ত।


2
Sp_getapplock হিসাবে +1 একটি দরকারী সরঞ্জাম যা সুপরিচিত নয়। একটি 'অরিভাল জগাখিচুড়ি দেওয়া হতে পারে যা আলাদা হতে সময় নিতে পারে, এটি একটি প্রক্রিয়াটি ডেডলকিংয়ের সিরিয়ালিয়াল করার একটি সহজ কৌশল। তবে, সহজেই বোঝা যায় এমন একটি মামলার জন্য এটি কি প্রথম পছন্দ হওয়া উচিত এবং (সম্ভবত) স্ট্যান্ডার্ড লকিংয়ের ব্যবস্থা দ্বারা মোকাবেলা করা যেতে পারে?
মার্ক স্টোরি-স্মিথ

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

আমি অনুমান করি যে আমি স্পষ্ট কিছু মিস করছি, তবে কীভাবে UPDATE dbo.DeadlockTestMutex SET Toggle = 1 - Toggle WHERE ID = 1;ডেডলকগুলি আটকাতে হবে?
ডেল কে

9

XLOCKআপনার SELECTপদ্ধতির বা নীচের যে কোনও একটিতে ইঙ্গিতটির ব্যবহার UPDATEএই ধরণের ডেডলকের প্রতিরোধী হওয়া উচিত:

DECLARE @Output TABLE ([NewId] INT);
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

BEGIN TRANSACTION;

UPDATE
    dbo.tblIDs WITH (XLOCK)
SET 
    LastID = LastID + 1
OUTPUT
    INSERTED.[LastId] INTO @Output
WHERE
    IDName = @IDName;

IF(@@ROWCOUNT = 1)
BEGIN
    SELECT @NewId = [NewId] FROM @Output;
END
ELSE
BEGIN
    SET @NewId = 1;

    INSERT dbo.tblIDs
        (IDName, LastID)
    VALUES
        (@IDName, @NewId);
END

SELECT [NewId] = @NewId ;

COMMIT TRANSACTION;

বেশ কয়েকটি অন্যান্য রূপ নিয়ে ফিরে আসবে (যদি এটির কাছে না হয় তবে!))


যখন XLOCKকোনও বিদ্যমান কাউন্টারকে একাধিক সংযোগ থেকে আপডেট হওয়া থেকে বিরত রাখবে, তখন কি আপনার TABLOCKXএকাধিক সংযোগ একই নতুন কাউন্টার যুক্ত করা থেকে বিরত রাখার প্রয়োজন হবে না ?
ডেল কে

1
@ ডেলবুরেল নো, আপনার আইডি নেমে পিকে বা অনন্য বাধা থাকবে।
মার্ক স্টোরি-স্মিথ

7

মাইক ডিফার খুব হালকা উপায়ে এটি সম্পাদন করার জন্য আমাকে মার্জিত উপায় দেখিয়েছেন:

ALTER PROCEDURE [dbo].[GetNextID](
    @IDName nvarchar(255)
)
AS
BEGIN
    /*
        Description:    Increments and returns the LastID value from tblIDs for a given IDName
        Author:         Max Vernon / Mike Defehr
        Date:           2012-07-19

    */

    DECLARE @Retry int;
    DECLARE @EN int, @ES int, @ET int;
    SET @Retry = 5;
    DECLARE @NewID int;
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET NOCOUNT ON;
    WHILE @Retry > 0
    BEGIN
        BEGIN TRY
            UPDATE dbo.tblIDs 
            SET @NewID = LastID = LastID + 1 
            WHERE IDName = @IDName;

            IF @NewID IS NULL
            BEGIN
                SET @NewID = 1;
                INSERT INTO tblIDs (IDName, LastID) VALUES (@IDName, @NewID);
            END
            SET @Retry = -2; /* no need to retry since the operation completed */
        END TRY
        BEGIN CATCH
            IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
                SET @Retry = @Retry - 1;
            ELSE
                BEGIN
                SET @Retry = -1;
                SET @EN = ERROR_NUMBER();
                SET @ES = ERROR_SEVERITY();
                SET @ET = ERROR_STATE()
                RAISERROR (@EN,@ES,@ET);
                END
        END CATCH
    END
    IF @Retry = 0 /* must have deadlock'd 5 times. */
    BEGIN
        SET @EN = 1205;
        SET @ES = 13;
        SET @ET = 1
        RAISERROR (@EN,@ES,@ET);
    END
    ELSE
        SELECT @NewID AS NewID;
END
GO

(সম্পূর্ণতার জন্য, এখানে সারণীটি সঞ্চিত প্রোকের সাথে সম্পর্কিত রয়েছে)

CREATE TABLE [dbo].[tblIDs]
(
    IDName nvarchar(255) NOT NULL,
    LastID int NULL,
    CONSTRAINT [PK_tblIDs] PRIMARY KEY CLUSTERED 
    (
        [IDName] ASC
    ) WITH 
    (
        PAD_INDEX = OFF
        , STATISTICS_NORECOMPUTE = OFF
        , IGNORE_DUP_KEY = OFF
        , ALLOW_ROW_LOCKS = ON
        , ALLOW_PAGE_LOCKS = ON
        , FILLFACTOR = 100
    ) 
);
GO

এটি সর্বশেষতম সংস্করণে কার্যকর করার পরিকল্পনা:

এখানে চিত্র বর্ণনা লিখুন

এবং এটি হ'ল মূল সংস্করণ (অচলিত সংবেদনশীল) এর বাস্তবায়ন পরিকল্পনা:

এখানে চিত্র বর্ণনা লিখুন

স্পষ্টতই, নতুন সংস্করণ জিতেছে!

তুলনার জন্য, (XLOCK)ইত্যাদির সাথে অন্তর্বর্তী সংস্করণ নিম্নলিখিত পরিকল্পনাটি তৈরি করে:

এখানে চিত্র বর্ণনা লিখুন

আমি বলব যে এটি একটি জয়! প্রত্যেকের সহায়তার জন্য ধন্যবাদ!


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

2
SERIALIZABLEভুতুড়েদের প্রতিরোধের জন্য অস্তিত্ব নেই। এটি সিরিয়ালাইজযোগ্য বিচ্ছিন্ন শব্দার্থবিদ্যা সরবরাহ করার জন্য উপস্থিত রয়েছে , অর্থাত্ ডাটাবেসগুলিতে একই ধ্রুবক প্রভাব যেমন জড়িত লেনদেনগুলি সিরিয়ালিভাবে কিছু অনির্দিষ্ট ক্রমে সম্পাদিত হয়েছিল।
পল হোয়াইট GoFundMonica বলেছেন

6

মার্ক স্টোরি-স্মিথের বজ্রপাত চুরি করতে নয়, তবে তিনি উপরে তার পোস্টটি সহ কিছু নিয়ে আছেন (যা ঘটনাক্রমে সর্বাধিক উত্সাহ পেয়েছে)। আমি ম্যাক্সকে যে পরামর্শ দিয়েছিলাম তা "আপডেটের সেট @ পরিবর্তনশীল = কলাম = কলাম + মান" এর আশেপাশে কেন্দ্রিক ছিল যা আমি সত্যিই শীতল বলে মনে করি, তবে আমি মনে করি এটি অনিবন্ধিত হতে পারে (এটি সমর্থন করাতে হবে, যদিও এটি টিসিপি-র জন্য বিশেষত রয়েছে) benchmarks)।

এখানে মার্কের উত্তরের একটি প্রকরণ রয়েছে - কারণ আপনি নতুন আইডি মানটি রেকর্ডসেট হিসাবে ফিরিয়ে দিচ্ছেন, আপনি স্কেলার ভেরিয়েবলটি পুরোপুরি সরিয়ে ফেলতে পারবেন, কোনও স্পষ্ট লেনদেনের প্রয়োজন হবে না, এবং আমি সম্মত হব যে বিচ্ছিন্নতা স্তরগুলি নিয়ে গণ্ডগোল করা অপ্রয়োজনীয় I যেমন. ফলাফলটি খুব পরিষ্কার এবং চতুর ...

ALTER PROC [dbo].[GetNextID]
  @IDName nvarchar(255)
  AS
BEGIN
SET NOCOUNT ON;

DECLARE @Output TABLE ([NewID] INT);

UPDATE dbo.tblIDs SET LastID = LastID + 1
OUTPUT inserted.[LastId] INTO @Output
WHERE IDName = @IDName;

IF(@@ROWCOUNT = 1)
    SELECT [NewID] FROM @Output;
ELSE
    INSERT dbo.tblIDs (IDName, LastID)
    OUTPUT INSERTED.LastID AS [NewID]
    VALUES (@IDName,1);
END

3
সম্মতি জানানো হয়েছে যে এটি অচলাবস্থার জন্য প্রতিরোধী হওয়া উচিত তবে যদি আপনি লেনদেন বাদ দেন তবে এটি সন্নিবেশের একটি রেসের শর্তে প্রবণ।
মার্ক স্টোরী-স্মিথ

4

আমি এটি পরিবর্তন করে গত বছরে একটি সিস্টেমে অনুরূপ অচলাবস্থা ঠিক করেছি:

IF (SELECT COUNT(IDName) FROM tblIDs WHERE IDName = @IDName) = 0 
  INSERT INTO tblIDs (IDName, LastID) VALUES (@IDName, @NewID)
ELSE
  UPDATE tblIDs SET LastID = @NewID WHERE IDName = @IDName;

এটি:

UPDATE tblIDs SET LastID = @NewID WHERE IDName = @IDName;
IF @@ROWCOUNT = 0
BEGIN
  INSERT ...
END

সাধারণভাবে, COUNTউপস্থিতি বা অনুপস্থিতি নির্ধারণের জন্য বিচারক নির্বাচন করা যথেষ্ট ব্যর্থ। এই ক্ষেত্রে যেহেতু এটি 0 বা 1 হয় এটি অনেক বেশি কাজ করার মতো নয় তবে (ক) সেই অভ্যাসটি অন্যান্য ক্ষেত্রে রক্তক্ষরণ করতে পারে যেখানে এটি অনেক ব্যয়বহুল হবে (এই ক্ষেত্রেগুলি IF NOT EXISTSপরিবর্তে ব্যবহার করুন IF COUNT() = 0) এবং (খ) অতিরিক্ত স্ক্যান সম্পূর্ণ অপ্রয়োজনীয়। UPDATEসঞ্চালিত মূলত একই চেক।

এছাড়াও, এটি আমার কাছে গুরুতর কোড গন্ধের মতো দেখাচ্ছে:

SET @NewID = COALESCE((SELECT LastID FROM tblIDs WHERE IDName = @IDName),0)+1;

এখানে কী? কেন কেবল কোনও পরিচয় কলাম ব্যবহার করবেন না বা ROW_NUMBER()ক্যোয়ারির সময় ব্যবহার করে সেই ক্রমটি সন্ধান করবেন ?


আমাদের কাছে থাকা বেশিরভাগ টেবিলগুলি একটি ব্যবহার করছে IDENTITY। এই টেবিলটি এমএস অ্যাক্সেসে লিখিত কিছু লিগ্যাসি কোড সমর্থন করে যা পুনঃনির্মাণে মোটামুটি জড়িত। SET @NewID=লাইন কেবল (কিন্তু আপনি ইতিমধ্যে জানি) দেওয়া আইডির জন্য সারণিতে সংরক্ষিত মান বৃদ্ধি হবে। আমি কীভাবে ব্যবহার করতে পারি তার উপর আপনি কী প্রসারিত করতে পারেন ROW_NUMBER()?
ম্যাক্স ভার্নন

@ ম্যাক্স ভার্নন LastIDআপনার মডেলটির প্রকৃত অর্থ কী তা জেনে না । এর উদ্দেশ্য কী? নামটি হুবহু স্ব-ব্যাখ্যামূলক নয়। অ্যাক্সেস কীভাবে এটি ব্যবহার করে?
অ্যারন বারট্র্যান্ড

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

আমি আপনার পরিবর্তন বাস্তবায়ন করব। "কম বেশি" এর খাঁটি কেস!
ম্যাক্স ভার্নন

1
আপনার স্থির অচলাবস্থা আবার উদ্ভূত হতে পারে। আপনার দ্বিতীয় প্যাটার্নটিও ঝুঁকিপূর্ণ: sqlblog.com/blogs/alexender_kuznetsov/archive/2010/01/12/… অচলাবস্থা দূর করতে আমি স্প_গেটঅ্যাপলক ব্যবহার করব। কয়েকশ ব্যবহারকারীর সাথে মিশ্রিত লোড সিস্টেমটিতে ডেডলক না থাকতে পারে।
একে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.