सबसे अधिक संभावना है कि आप दौड़ की स्थिति में भाग रहे हैं . जब आप अलग लेन-देन . में अपने फ़ंक्शन को एक के बाद एक 1000 बार त्वरित क्रम में चलाते हैं , कुछ ऐसा होता है:
T1 T2 T3 ...
SELECT max(id) -- id 1
SELECT max(id) -- id 1
SELECT max(id) -- id 1
...
Row id 1 locked, wait ...
Row id 1 locked, wait ...
UPDATE id 1
...
COMMIT
Wake up, UPDATE id 1 again!
COMMIT
Wake up, UPDATE id 1 again!
COMMIT
...
SQL फ़ंक्शन के रूप में बड़े पैमाने पर पुनर्लेखित और सरलीकृत:
CREATE OR REPLACE FUNCTION get_result(val1 text, val2 text)
RETURNS text AS
$func$
UPDATE table t
SET id_used = 'Y'
, col1 = val1
, id_used_date = now()
FROM (
SELECT id
FROM table
WHERE id_used IS NULL
AND id_type = val2
ORDER BY id
LIMIT 1
FOR UPDATE -- lock to avoid race condition! see below ...
) t1
WHERE t.id_type = val2
-- AND t.id_used IS NULL -- repeat condition (not if row is locked)
AND t.id = t1.id
RETURNING id;
$func$ LANGUAGE sql;
बहुत अधिक स्पष्टीकरण के साथ संबंधित प्रश्न:
व्याख्या करें
-
दो अलग SQL कथन न चलाएँ। यह अधिक महंगा है और दौड़ की स्थिति के लिए समय सीमा को बढ़ाता है। एक
UPDATE
एक सबक्वेरी के साथ बहुत बेहतर है। -
सरल कार्य के लिए आपको PL/pgSQL की आवश्यकता नहीं है। आप अब भी कर सकते हैं पीएल/पीजीएसक्यूएल का उपयोग करें,
UPDATE
वही रहता है। -
दौड़ की स्थिति से बचाव के लिए आपको चयनित पंक्ति को लॉक करना होगा। लेकिन आप इसे उस समग्र कार्य के साथ नहीं कर सकते, जिसका आप नेतृत्व करते हैं, क्योंकि प्रति दस्तावेज़ :
-
बोल्ड जोर मेरा। सौभाग्य से, आप
min(id)
. को बदल सकते हैं आसानी से समतुल्यORDER BY
. के साथ /LIMIT 1
मैंने ऊपर प्रदान किया। इंडेक्स का भी उपयोग कर सकते हैं। -
अगर टेबल बड़ी है, तो आपको जरूरत
id
. पर एक इंडेक्स कम से कम। मान लें किid
पहले से हीPRIMARY KEY
के रूप में अनुक्रमित है , इससे मदद मिलेगी। लेकिन यह अतिरिक्त आंशिक बहु-स्तंभ अनुक्रमणिका शायद और भी बहुत कुछ में मदद करेगा :CREATE INDEX foo_idx ON table (id_type, id) WHERE id_used IS NULL;
वैकल्पिक समाधान
सलाहकार ताले यहां बेहतर तरीका हो सकता है:
या हो सकता है कि आप एक साथ कई पंक्तियों को लॉक करना चाहें :