PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

अगली प्राथमिक कुंजी की गणना करें - विशिष्ट प्रारूप की

यह गैपलेस अनुक्रम समस्या का एक प्रकार जैसा दिखता है; यहाँ भी देखा गया।

गैपलेस दृश्यों में गंभीर प्रदर्शन और समवर्ती समस्याएं होती हैं।

इस बारे में बहुत गंभीरता से सोचें कि क्या होगा जब एक साथ कई इंसर्ट होंगे। आपको असफल इन्सर्ट के लिए फिर से प्रयास करने के लिए तैयार रहना होगा, या LOCK TABLE myTable IN EXCLUSIVE MODE INSERT . से पहले इसलिए केवल एक INSERT एक समय में उड़ान में हो सकता है।

पंक्ति लॉकिंग वाली अनुक्रम तालिका का उपयोग करें

इस स्थिति में मैं क्या करूँगा:

CREATE TABLE sequence_numbers(
    level integer,
    code integer,
    next_value integer DEFAULT 0 NOT NULL,
    PRIMARY KEY (level,code),
    CONSTRAINT level_must_be_one_digit CHECK (level BETWEEN 0 AND 9),
    CONSTRAINT code_must_be_three_digits CHECK (code BETWEEN 0 AND 999),
    CONSTRAINT value_must_be_four_digits CHECK (next_value BETWEEN 0 AND 9999)
);

INSERT INTO sequence_numbers(level,code) VALUES (2,777);

CREATE OR REPLACE FUNCTION get_next_seqno(level integer, code integer)
RETURNS integer LANGUAGE 'SQL' AS $$
    UPDATE sequence_numbers 
    SET next_value = next_value + 1
    WHERE level = $1 AND code = $2
    RETURNING (to_char(level,'FM9')||to_char(code,'FM000')||to_char(next_value,'FM0000'))::integer;
$$;

फिर एक आईडी प्राप्त करने के लिए:

INSERT INTO myTable (sequence_number, blah)
VALUES (get_next_seqno(2,777), blah);

इस दृष्टिकोण का अर्थ है कि केवल एक लेन-देन एक समय में किसी दिए गए (स्तर, मोड) जोड़ी के साथ एक पंक्ति सम्मिलित कर सकता है, लेकिन मुझे लगता है कि यह दौड़-मुक्त है।

गतिरोध से सावधान रहें

अभी भी एक समस्या है जहां दो समवर्ती लेन-देन गतिरोध कर सकते हैं यदि वे एक अलग क्रम में पंक्तियों को सम्मिलित करने का प्रयास करते हैं। इसके लिए कोई आसान समाधान नहीं है; आपको या तो अपने इन्सर्ट को ऑर्डर करना होगा ताकि आप हमेशा हाई से पहले लो लेवल और मोड डालें, प्रति ट्रांजेक्शन एक इंसर्ट करें, या डेडलॉक के साथ रहें और पुनः प्रयास करें। व्यक्तिगत रूप से मैं बाद वाला करूँगा।

समस्या का उदाहरण, दो psql सत्रों के साथ। सेटअप है:

CREATE TABLE myTable(seq_no integer primary key);
INSERT INTO sequence_numbers VALUES (1,666)

फिर दो सत्रों में:

SESSION 1                       SESSION 2

BEGIN;
                                BEGIN;

INSERT INTO myTable(seq_no)
VALUES(get_next_seqno(2,777));
                                INSERT INTO myTable(seq_no)
                                VALUES(get_next_seqno(1,666));

                                INSERT INTO myTable(seq_no)
                                VALUES(get_next_seqno(2,777));

INSERT INTO myTable(seq_no)
VALUES(get_next_seqno(1,666));

आप देखेंगे कि सत्र 2 में दूसरा इंसर्ट वापस आए बिना लटका रहेगा, क्योंकि यह सत्र 1 द्वारा आयोजित लॉक पर प्रतीक्षा कर रहा है। जब सत्र 1 अपने दूसरे इंसर्ट में सत्र 2 द्वारा आयोजित लॉक प्राप्त करने का प्रयास करता है, तो यह भी होगा टांगना। कोई प्रगति नहीं की जा सकती है, इसलिए एक या दो सेकंड के बाद PostgreSQL गतिरोध का पता लगाएगा और एक लेन-देन को रद्द कर देगा, जिससे दूसरे को आगे बढ़ने की अनुमति मिल जाएगी:

ERROR:  deadlock detected
DETAIL:  Process 16723 waits for ShareLock on transaction 40450; blocked by process 18632.
Process 18632 waits for ShareLock on transaction 40449; blocked by process 16723.
HINT:  See server log for query details.
CONTEXT:  SQL function "get_next_seqno" statement 1

आपका कोड या तो इसे संभालने के लिए तैयार रहना चाहिए और संपूर्ण लेन-देन . के लिए पुन:प्रयास करना चाहिए , या इसे एकल-सम्मिलित लेनदेन या सावधानीपूर्वक आदेश का उपयोग करके गतिरोध से बचना चाहिए।

स्वचालित रूप से गैर-मौजूद (स्तर, कोड) जोड़े बनाना

BTW, यदि आप (स्तर, कोड) संयोजन चाहते हैं जो sequence_numbers में पहले से मौजूद नहीं है तालिका को पहले उपयोग पर बनाया जाना है, यह आश्चर्यजनक रूप से सही होने के लिए जटिल है क्योंकि यह अप्सर्ट समस्या का एक प्रकार है। मैं व्यक्तिगत रूप से get_next_seqno . को संशोधित करूंगा/करूंगी इस तरह दिखने के लिए:

CREATE OR REPLACE FUNCTION get_next_seqno(level integer, code integer)
RETURNS integer LANGUAGE 'SQL' AS $$

    -- add a (level,code) pair if it isn't present.
    -- Racey, can fail, so you have to be prepared to retry
    INSERT INTO sequence_numbers (level,code)
    SELECT $1, $2
    WHERE NOT EXISTS (SELECT 1 FROM sequence_numbers WHERE level = $1 AND code = $2);

    UPDATE sequence_numbers 
    SET next_value = next_value + 1
    WHERE level = $1 AND code = $2
    RETURNING (to_char(level,'FM9')||to_char(code,'FM000')||to_char(next_value,'FM0000'))::integer;

$$;

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

ERROR:  duplicate key value violates unique constraint "sequence_numbers_pkey"
DETAIL:  Key (level, code)=(0, 555) already exists.
CONTEXT:  SQL function "get_next_seqno" statement 1


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL में किसी तालिका की पंक्ति गणना खोजने का तेज़ तरीका

  2. हेरोकू, पोस्टग्रेएसक्यूएल, डीजेंगो, टिप्पणियां, स्वादिष्ट:कोई भी ऑपरेटर दिए गए नाम और तर्क प्रकार से मेल नहीं खाता। आपको स्पष्ट प्रकार के कास्ट जोड़ने की आवश्यकता हो सकती है

  3. PostgreSQL में सभी कार्यों को सूचीबद्ध करने के 3 तरीके

  4. मैं PostgreSQL क्वेरी के हिस्से के रूप में एक पूर्णांक को स्ट्रिंग में कैसे परिवर्तित करूं?

  5. एक तालिका में प्रारंभ और समाप्ति तिथि से Postgres में Generate_series