ट्रिगर का उपयोग करके सिंक्रनाइज़ेशन


11

मुझे पिछली चर्चाओं के समान एक आवश्यकता है:

मेरे पास दो टेबल हैं, [Account].[Balance]और [Transaction].[Amount]:

CREATE TABLE Account (
      AccountID    INT
    , Balance      MONEY
);

CREATE TABLE Transaction (
      TransactionID INT
     , AccountID    INT
    , Amount      MONEY
);

जब [Transaction]तालिका के विरुद्ध कोई प्रविष्टि, अद्यतन या हटाना हो , तो उसके [Account].[Balance]आधार पर अद्यतन किया जाना चाहिए [Amount]

वर्तमान में मेरे पास यह काम करने के लिए एक ट्रिगर है:

ALTER TRIGGER [dbo].[TransactionChanged] 
ON  [dbo].[Transaction]
AFTER INSERT, UPDATE, DELETE
AS 
BEGIN
IF  EXISTS (select 1 from [Deleted]) OR EXISTS (select 1 from [Inserted])
    UPDATE [dbo].[Account]
    SET
    [Account].[Balance] = [Account].[Balance] + 
        (
            Select ISNULL(Sum([Inserted].[Amount]),0)
            From [Inserted] 
            Where [Account].[AccountID] = [Inserted].[AccountID]
        )
        -
        (
            Select ISNULL(Sum([Deleted].[Amount]),0)
            From [Deleted] 
            Where [Account].[AccountID] = [Deleted].[AccountID]
        )
END

हालांकि यह काम करने लगता है, मेरे सवाल हैं:

  1. क्या ट्रिगर रिलेशनल डेटाबेस के ACID सिद्धांत का अनुसरण करता है? वहाँ एक मौका है एक डालने के लिए प्रतिबद्ध हो सकता है, लेकिन ट्रिगर विफल?
  2. मेरे IFऔर UPDATEकथन अजीब लगते हैं। क्या सही [Account]पंक्ति को अपडेट करने का कोई बेहतर तरीका है ?

जवाबों:


13

1. क्या ट्रिगर रिलेशनल डेटाबेस के ACID सिद्धांत का अनुसरण करता है? वहाँ एक मौका है एक डालने के लिए प्रतिबद्ध हो सकता है, लेकिन ट्रिगर विफल?

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

एसिड गुण भी पूरे लेन-देन (ट्रिगर कोड सहित) की गारंटी एक राज्य है कि उल्लंघन नहीं करता है किसी भी स्पष्ट की कमी (में डेटाबेस छोड़ देंगे अनुरूप ) और किसी भी वसूली योग्य प्रतिबद्ध प्रभाव एक डेटाबेस दुर्घटना (बच जाएगा टिकाऊ )।

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

2. मेरे विचार और अद्यतन कथन अजीब लगते हैं। क्या सही [खाता] पंक्ति को अपडेट करने का कोई बेहतर तरीका है?

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

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

कुछ समस्याओं का वर्णन करने के लिए, मैं नीचे कुछ नमूना कोड दिखाता हूं। यह एक कठोर-परीक्षणित समाधान नहीं है (ट्रिगर कठिन हैं!) और मैं आपको यह सुझाव नहीं दे रहा हूं कि आप इसे सीखने की कवायद के अलावा किसी अन्य चीज के रूप में उपयोग करें। एक वास्तविक प्रणाली के लिए, गैर-ट्रिगर समाधान के महत्वपूर्ण लाभ हैं, इसलिए आपको अन्य प्रश्न के उत्तर की सावधानीपूर्वक समीक्षा करनी चाहिए , और ट्रिगर विचार से पूरी तरह से बचना चाहिए।

नमूना तालिकाओं

CREATE TABLE dbo.Accounts
(
    AccountID integer NOT NULL,
    Balance money NOT NULL,

    CONSTRAINT PK_Accounts_ID
    PRIMARY KEY CLUSTERED (AccountID)
);

CREATE TABLE dbo.Transactions
(
    TransactionID integer IDENTITY NOT NULL,
    AccountID integer NOT NULL,
    Amount money NOT NULL,

    CONSTRAINT PK_Transactions_ID
    PRIMARY KEY CLUSTERED (TransactionID),

    CONSTRAINT FK_Accounts
    FOREIGN KEY (AccountID)
    REFERENCES dbo.Accounts (AccountID)
);

रोकथाम TRUNCATE TABLE

ट्रिगर से फायर नहीं किया जाता है TRUNCATE TABLE। निम्न खाली तालिका विशुद्ध रूप से Transactionsतालिका को छंटनी से बचाने के लिए मौजूद है (एक विदेशी कुंजी द्वारा संदर्भित होने से तालिका छंटनी को रोकता है):

CREATE TABLE dbo.PreventTransactionsTruncation
(
    Dummy integer NULL,

    CONSTRAINT FK_Transactions
    FOREIGN KEY (Dummy)
    REFERENCES dbo.Transactions (TransactionID),

    CONSTRAINT CHK_NoRows
    CHECK (Dummy IS NULL AND Dummy IS NOT NULL)
);

ट्रिगर परिभाषा

निम्न ट्रिगर कोड सुनिश्चित करता है कि केवल आवश्यक खाता प्रविष्टियाँ ही बनी हुई हैं, और SERIALIZABLEवहां शब्दार्थ का उपयोग किया जाता है। एक वांछनीय साइड-इफेक्ट के रूप में, यह गलत परिणामों से भी बचता है जो एक पंक्ति-संस्करण अलगाव स्तर का उपयोग होने पर परिणाम हो सकता है। कोड भी ट्रिगर कोड को निष्पादित करने से बचता है यदि स्रोत विवरण से कोई पंक्तियां प्रभावित नहीं हुई थीं। अस्थायी तालिका और RECOMPILEसंकेत का उपयोग गलत तरीके से किए गए कार्डिनैलिटी अनुमानों के कारण होने वाली निष्पादन योजना की समस्याओं से बचने के लिए किया जाता है:

CREATE TRIGGER dbo.TransactionChange ON dbo.Transactions 
AFTER INSERT, UPDATE, DELETE 
AS
BEGIN
IF @@ROWCOUNT = 0 OR
    TRIGGER_NESTLEVEL
    (
        OBJECT_ID(N'dbo.TransactionChange', N'TR'),
        'AFTER', 
        'DML'
    ) > 1 
    RETURN;

    SET NOCOUNT, XACT_ABORT ON;

    CREATE TABLE #Delta
    (
        AccountID integer PRIMARY KEY,
        Amount money NOT NULL
    );

    INSERT #Delta
        (AccountID, Amount)
    SELECT 
        InsDel.AccountID,
        Amount = SUM(InsDel.Amount)
    FROM 
    (
        SELECT AccountID, Amount
        FROM Inserted
        UNION ALL
        SELECT AccountID, $0 - Amount
        FROM Deleted
    ) AS InsDel
    GROUP BY
        InsDel.AccountID;

    UPDATE A
    SET Balance += D.Amount
    FROM #Delta AS D
    JOIN dbo.Accounts AS A WITH (SERIALIZABLE)
        ON A.AccountID = D.AccountID
    OPTION (RECOMPILE);
END;

परिक्षण

निम्न कोड शून्य बैलेंस के साथ 100,000 खाते बनाने के लिए संख्याओं की तालिका का उपयोग करता है :

INSERT dbo.Accounts
    (AccountID, Balance)
SELECT
    N.n, $0
FROM dbo.Numbers AS N
WHERE
    N.n BETWEEN 1 AND 100000;

नीचे दिए गए परीक्षण कोड में 10,000 यादृच्छिक लेनदेन सम्मिलित हैं:

INSERT dbo.Transactions
    (AccountID, Amount)
SELECT 
    CONVERT(integer, RAND(CHECKSUM(NEWID())) * 100000 + 1),
    CONVERT(money, RAND(CHECKSUM(NEWID())) * 500 - 250)
FROM dbo.Numbers AS N
WHERE 
    N.n BETWEEN 1 AND 10000;

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

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