समवर्ती लेखन पहुंच के बिना
एक सीटीई में एक चयन करें और इसके 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 LOCKED
PostgreSQL 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)
- साथ वितरित करें।
चूंकि यह एक मात्रा है, यह सैद्धांतिक रूप से अतिप्रवाह है । यदि आप पर्याप्त पागल हैं, तो इसके बजाय का उपयोग करें - वास्तव में पागल के लिए एक सैद्धांतिक "हैश टक्कर" छोड़कर ...id
integer
tableoid
bigint
integer
(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()
। नियम पुस्तिका:
एक बार सत्र स्तर पर अधिग्रहित होने के बाद, एक सलाहकार ताला स्पष्ट रूप से जारी होने या सत्र समाप्त होने तक आयोजित किया जाता है।
सम्बंधित: