अद्यतन पोस्ट ... सीमा 1


77

मेरे पास एक पोस्टग्रैज डेटाबेस है जिसमें सर्वर के क्लस्टर पर विवरण हैं, जैसे कि सर्वर की स्थिति ('सक्रिय', 'स्टैंडबाय' आदि)। किसी भी समय सक्रिय सर्वर को एक स्टैंडबाय पर विफल होने की आवश्यकता हो सकती है, और मुझे परवाह नहीं है कि विशेष रूप से कौन से स्टैंडबाय का उपयोग किया जाता है।

मैं एक स्टैंडबाय - जस्ट वन - की स्थिति बदलने के लिए एक डेटाबेस क्वेरी चाहता हूं और उपयोग किए जाने वाले सर्वर आईपी को वापस करता हूं। मनमाना हो सकता है: चूंकि सर्वर की स्थिति क्वेरी के साथ बदलती है, इससे कोई फर्क नहीं पड़ता कि कौन सा स्टैंडबाय चुना गया है।

क्या मेरी क्वेरी को केवल एक अपडेट तक सीमित करना संभव है?

यह है, जो कि अभी तक मेरे पास है:

UPDATE server_info SET status = 'active' 
WHERE status = 'standby' [[LIMIT 1???]] 
RETURNING server_ip;

Postgres को यह पसंद नहीं है। मैं अलग तरह से क्या कर सकता था?


बस सर्वर को कोड में चुनें और इसे एक विवश के रूप में जोड़ें। यह आपको अतिरिक्त परिस्थितियों (सबसे पुराना, नवीनतम, सबसे हाल ही में जीवित, कम से कम लोड, एक ही डीसी, विभिन्न रैक, कम से कम त्रुटियों) की जांच करने की अनुमति देता है। अधिकांश फेलओवर प्रोटोकॉल को वैसे भी नियतत्ववाद के किसी न किसी रूप की आवश्यकता होती है।
Eckes

@eckes यह एक दिलचस्प विचार है। मेरे मामले में "कोड में सर्वर को चुनना" का मतलब पहले डीबी से उपलब्ध सर्वरों की एक सूची पढ़ना होगा , और फिर एक रिकॉर्ड अपडेट करना होगा। क्योंकि अनुप्रयोग के कई उदाहरण इस क्रिया को कर सकते हैं, एक दौड़ की स्थिति है और एक परमाणु ऑपरेशन की आवश्यकता है (या 5 साल पहले था)। पिक को नियतात्मक होने की आवश्यकता नहीं थी।

जवाबों:


125

समवर्ती लेखन पहुंच के बिना

एक सीटीई में एक चयन करें और इसके FROMखंड में शामिल हो जाएं UPDATE

WITH cte AS (
   SELECT server_ip          -- pk column or any (set of) unique column(s)
   FROM   server_info
   WHERE  status = 'standby'
   LIMIT  1                  -- arbitrary pick (cheapest)
   )
UPDATE server_info s
SET    status = 'active' 
FROM   cte
WHERE  s.server_ip = cte.server_ip
RETURNING server_ip;

मेरे पास मूल रूप से एक सादी उपश्रेणी थी, लेकिन यह LIMITकुछ क्वेरी योजनाओं के लिए फ़ेइक को इंगित कर सकता है:

योजनाकार एक योजना है कि ऊपर एक नेस्टेड पाश कार्यान्वित उत्पन्न करने के लिए चुन सकते हैं LIMITingसबक्वेरी, और अधिक के कारण UPDATEsसे LIMIT, जैसे:

 Update on buganalysis [...] rows=5
   ->  Nested Loop
         ->  Seq Scan on buganalysis
         ->  Subquery Scan on sub [...] loops=11
               ->  Limit [...] rows=2
                     ->  LockRows
                           ->  Sort
                                 ->  Seq Scan on buganalysis

परीक्षण के मामले को फिर से प्रस्तुत करना

उपर्युक्त को ठीक करने का तरीका यह था कि LIMITउप-वर्ग को अपने CTE में लपेट दिया जाए , क्योंकि CTE के भौतिक होने के कारण यह नेस्टेड लूप के विभिन्न पुनरावृत्तियों पर अलग-अलग परिणाम नहीं लौटाएगा।

या के साथ सरल मामले के लिएएक कम सहसंबद्ध उपशम का उपयोग करेंLIMIT 1। सरल, तेज:

UPDATE server_info
SET    status = 'active' 
WHERE  server_ip = (
         SELECT server_ip
         FROM   server_info
         WHERE  status = 'standby'
         LIMIT  1
         )
RETURNING server_ip;

समवर्ती लेखन पहुंच के साथ

इस सब के लिए डिफ़ॉल्ट अलगाव स्तरREAD COMMITTED मान । Stricter अलगाव स्तर ( REPEATABLE READऔर SERIALIZABLE) अभी भी क्रमांकन त्रुटियों में परिणाम कर सकते हैं। देख:

समवर्ती लेखन लोड के तहत, FOR UPDATE SKIP LOCKEDदौड़ की स्थिति से बचने के लिए पंक्ति को लॉक करने के लिए जोड़ें । SKIP LOCKEDपोस्टग्रेज 9.5 में जोड़ा गया था , पुराने संस्करणों के लिए नीचे देखें। नियम पुस्तिका:

के साथ SKIP LOCKED, किसी भी चयनित पंक्तियों को तुरंत बंद नहीं किया जा सकता है। लॉक की गई पंक्तियों को छोड़ना डेटा का एक असंगत दृश्य प्रदान करता है, इसलिए यह सामान्य प्रयोजन के काम के लिए उपयुक्त नहीं है, लेकिन एक कतार-जैसी तालिका तक पहुंचने वाले कई उपभोक्ताओं के साथ लॉक विवाद से बचने के लिए उपयोग किया जा सकता है।

UPDATE server_info
SET    status = 'active' 
WHERE  server_ip = (
         SELECT server_ip
         FROM   server_info
         WHERE  status = 'standby'
         LIMIT  1
         FOR    UPDATE SKIP LOCKED
         )
RETURNING server_ip;

यदि कोई योग्यता नहीं है, तो पंक्ति को छोड़ दिया गया है, इस क्वेरी में कुछ भी नहीं होता है (कोई पंक्ति अपडेट नहीं की जाती है) और आपको एक खाली परिणाम मिलता है। अनियंत्रित संचालन के लिए जिसका अर्थ है कि आप कर रहे हैं।

हालाँकि, समवर्ती लेन-देन में लॉक पंक्तियाँ हो सकती हैं, लेकिन फिर अपडेट ( ROLLBACKया अन्य कारणों) को पूरा न करें। अंतिम जांच चलाने के लिए सुनिश्चित करें:

SELECT NOT EXISTS (
   SELECT 1
   FROM   server_info
   WHERE  status = 'standby'
   );

SELECTबंद पंक्तियों को भी देखता है। वेइल जो वापस नहीं आता है true, एक या अधिक पंक्तियों को अभी भी संसाधित किया जा रहा है और लेनदेन अभी भी वापस लुढ़का जा सकता है। (या इस बीच नई पंक्तियाँ जोड़ दी गई हैं।) थोड़ा इंतजार करें, फिर दो चरणों को लूप करें: ( UPDATEजब तक आपको कोई पंक्ति वापस नहीं मिलती; SELECT...) जब तक आप नहीं मिलते true

सम्बंधित:

बिना SKIP LOCKEDPostgreSQL 9.4 या पुराने में

UPDATE server_info
SET    status = 'active' 
WHERE  server_ip = (
         SELECT server_ip
         FROM   server_info
         WHERE  status = 'standby'
         LIMIT  1
         FOR    UPDATE
         )
RETURNING server_ip;

एक ही पंक्ति को लॉक करने की कोशिश कर रहे समवर्ती लेनदेन तब तक अवरुद्ध होते हैं जब तक कि पहला अपना लॉक जारी नहीं कर देता।

यदि पहले वापस लुढ़का हुआ था, तो अगला लेनदेन लॉक लेता है और सामान्य रूप से आगे बढ़ता है; कतार में अन्य लोग इंतजार करते रहते हैं।

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

हम सलाहकार ताले की मदद से स्थिति को अनब्लॉक कर सकते हैं :

UPDATE server_info
SET    status = 'active' 
WHERE  server_ip = (
         SELECT server_ip
         FROM   server_info
         WHERE  status = 'standby'
         AND    pg_try_advisory_xact_lock(id)
         LIMIT  1
         FOR    UPDATE
         )
RETURNING server_ip;

इस तरह, अगली पंक्ति को अभी तक लॉक नहीं किया गया है, अपडेट किया जाएगा। प्रत्येक लेनदेन के साथ काम करने के लिए एक नई पंक्ति मिलती है। मैंने इस ट्रिक के लिए चेक पोस्टग्रिज विकी से मदद ली थी।

idकिसी भी अद्वितीय जा रहा है bigintस्तंभ (या की तरह एक अंतर्निहित कलाकारों के साथ किसी भी प्रकार int4या int2)।

यदि आपके डेटाबेस में कई तालिकाओं के लिए सलाहकार ताले का उपयोग किया जाता है, तो यहां एक अद्वितीय होने के साथ pg_try_advisory_xact_lock(tableoid::int, id)- साथ वितरित करें। चूंकि यह एक मात्रा है, यह सैद्धांतिक रूप से अतिप्रवाह है । यदि आप पर्याप्त पागल हैं, तो इसके बजाय का उपयोग करें - वास्तव में पागल के लिए एक सैद्धांतिक "हैश टक्कर" छोड़कर ...idinteger
tableoidbigintinteger(tableoid::bigint % 2147483648)::int

इसके अलावा, Postgres WHEREकिसी भी क्रम में शर्तों का परीक्षण करने के लिए स्वतंत्र है । यह हो सकता है परीक्षण pg_try_advisory_xact_lock()और एक ताला प्राप्त करने से पहले status = 'standby' , जो असंबंधित पंक्तियाँ, जहां पर अतिरिक्त सलाहकार ताले में परिणाम सकता है status = 'standby'सच नहीं है। SO पर संबंधित प्रश्न:

आमतौर पर, आप इसे अनदेखा कर सकते हैं। यह गारंटी देने के लिए कि केवल योग्य पंक्तियों को लॉक किया गया है, आप एक CTE में predicate (s) को घोंसले की तरह ऊपर कर सकते हैं या OFFSET 0हैक के साथ एक उपकुंजी (इनलाइनिंग को रोक सकते हैं) । उदाहरण:

या (अनुक्रमिक स्कैन के लिए सस्ता) एक CASEबयान में शर्तों को घोंसला दें जैसे:

WHERE  CASE WHEN status = 'standby' THEN pg_try_advisory_xact_lock(id) END

हालाँकि , CASEट्रिक भी Postgres को एक इंडेक्स का उपयोग करने से रोकती है status। यदि ऐसा सूचकांक उपलब्ध है, तो आपको शुरू करने के लिए अतिरिक्त घोंसले के शिकार की आवश्यकता नहीं है: केवल योग्यता पंक्तियों को सूचकांक स्कैन में बंद कर दिया जाएगा।

चूँकि आप यह सुनिश्चित नहीं कर सकते कि हर कॉल में एक इंडेक्स का उपयोग किया जाता है, आप बस:

WHERE  status = 'standby'
AND    CASE WHEN status = 'standby' THEN pg_try_advisory_xact_lock(id) END

CASEतार्किक रूप से बेमानी है, लेकिन यह सर्वर पर चर्चा उद्देश्य।

यदि कमांड लंबे लेनदेन का हिस्सा है, तो सत्र-स्तर के ताले पर विचार करें जो मैन्युअल रूप से जारी किए जा सकते हैं (और होना चाहिए)। तो जैसे ही आप लॉक की गई पंक्ति के साथ किया जाता है, आप अनलॉक कर सकते हैं: pg_try_advisory_lock()औरpg_advisory_unlock()नियम पुस्तिका:

एक बार सत्र स्तर पर अधिग्रहित होने के बाद, एक सलाहकार ताला स्पष्ट रूप से जारी होने या सत्र समाप्त होने तक आयोजित किया जाता है।

सम्बंधित:

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