एक ही कार्य के लिए एक साथ कॉल: गतिरोध कैसे हो रहे हैं?


15

मेरे new_customerआवेदन को वेब एप्लिकेशन द्वारा कई बार प्रति सेकंड (लेकिन केवल एक बार प्रति सत्र) कहा जाता है। पहली चीज़ जो यह करती है वह है customerटेबल को लॉक करना (यदि 'अस्तित्व में नहीं है तो एक इंसर्ट करने के लिए' - एक साधारण संस्करण upsert)।

डॉक्स के बारे में मेरी समझ यह है कि अन्य कॉल new_customerकेवल तब तक कतार में होनी चाहिए जब तक कि सभी पिछले कॉल समाप्त न हो जाएं:

लॉक टेबल एक टेबल-लेवल लॉक प्राप्त करता है, अगर किसी भी परस्पर विरोधी लॉक को रिलीज़ करने के लिए आवश्यक है, तो प्रतीक्षा करें।

इसके बजाय कभी-कभी गतिरोध क्यों होता है?

परिभाषा:

create function new_customer(secret bytea) returns integer language sql 
                security definer set search_path = postgres,pg_temp as $$
  lock customer in exclusive mode;
  --
  with w as ( insert into customer(customer_secret,customer_read_secret)
              select secret,decode(md5(encode(secret, 'hex')),'hex') 
              where not exists(select * from customer where customer_secret=secret)
              returning customer_id )
  insert into collection(customer_id) select customer_id from w;
  --
  select customer_id from customer where customer_secret=secret;
$$;

लॉग से त्रुटि:

2015-07-28 08:02:58 बीएसटी विवरण: प्रक्रिया 12380 डेटाबेस 12141 के 16438 के संबंध में विशेष विवरण के लिए इंतजार कर रहा है; 12379 प्रक्रिया द्वारा अवरुद्ध।
        प्रक्रिया 12379 डेटाबेस 12141 के संबंध में 16438 पर विशेष छूट का इंतजार करता है; 12380 प्रक्रिया द्वारा अवरुद्ध।
        प्रक्रिया 12380: new_customer (डिकोड ($ 1 :: पाठ, 'हेक्स') का चयन करें)
        प्रक्रिया 12379: new_customer (डिकोड ($ 1 :: पाठ, 'हेक्स') का चयन करें)
2015-07-28 08:02:58 BST HINT: क्वेरी विवरण के लिए सर्वर लॉग देखें।
2015-07-28 08:02:58 BST संपर्क: SQL फ़ंक्शन "new_customer" कथन 1
2015-07-28 08:02:58 BST स्टेटमेंट: new_customer (डिकोड ($ 1 :: टेक्स्ट, 'he')) का चयन करें

रिश्ता:

postgres=# select relname from pg_class where oid=16438;
┌──────────┐
 relname  
├──────────┤
 customer 
└──────────┘

संपादित करें:

मैं एक साधारण-ईश प्रतिलिपि प्रस्तुत करने योग्य परीक्षण केस प्राप्त करने में कामयाब रहा। मेरे लिए यह किसी प्रकार की दौड़ की स्थिति के कारण बग जैसा दिखता है।

स्कीमा:

create table test( id serial primary key, val text );

create function f_test(v text) returns integer language sql security definer set search_path = postgres,pg_temp as $$
  lock test in exclusive mode;
  insert into test(val) select v where not exists(select * from test where val=v);
  select id from test where val=v;
$$;

बैश स्क्रिप्ट दो बैश सत्रों में एक साथ चलती है:

for i in {1..1000}; do psql postgres postgres -c "select f_test('blah')"; done

त्रुटि लॉग (आमतौर पर 1000 कॉल से अधिक गतिरोध):

2015-07-28 16:46:19 BST ERROR:  deadlock detected
2015-07-28 16:46:19 BST DETAIL:  Process 9394 waits for ExclusiveLock on relation 65605 of database 12141; blocked by process 9393.
        Process 9393 waits for ExclusiveLock on relation 65605 of database 12141; blocked by process 9394.
        Process 9394: select f_test('blah')
        Process 9393: select f_test('blah')
2015-07-28 16:46:19 BST HINT:  See server log for query details.
2015-07-28 16:46:19 BST CONTEXT:  SQL function "f_test" statement 1
2015-07-28 16:46:19 BST STATEMENT:  select f_test('blah')

2 संपादित करें:

@ypercube नेlock table फ़ंक्शन के बाहर के साथ एक प्रकार का सुझाव दिया :

for i in {1..1000}; do psql postgres postgres -c "begin; lock test in exclusive mode; select f_test('blah'); end"; done

दिलचस्प बात यह गतिरोध को समाप्त करता है।


2
उसी फ़ंक्शन में, उस फ़ंक्शन में प्रवेश करने से पहले, customerएक तरह से उपयोग किया जाता है जो कमजोर लॉक को पकड़ लेगा? तब यह लॉक अपग्रेड की समस्या हो सकती है।
डैनियल वेरिटा

2
मैं यह नहीं समझा सकता। डैनियल एक बिंदु हो सकता है। Pgsql-general पर इसे बढ़ाने लायक हो सकता है। किसी भी तरह से, क्या आप आगामी Postgres 9.5 में UPSERT कार्यान्वयन के बारे में जानते हैं? डीपेज़ इस पर एक नज़र रखना।
एर्विन ब्रान्डस्टेट्टर

2
मेरा मतलब है कि एक ही लेन-देन के भीतर, न केवल एक ही सत्र (जैसा कि ताले टीएक्स अंत में जारी किया जाता है)। @Alexk का जवाब वही है जिसके बारे में मैं सोच रहा था, लेकिन अगर tx फ़ंक्शन के साथ शुरू और समाप्त होता है, जो गतिरोध की व्याख्या नहीं कर सकता है।
डैनियल वेटरे

1
@Erwin आप निस्संदेह मुझे उस जवाब में दिलचस्पी लेंगे जो मुझे
pgsql-

2
बहुत दिलचस्प है। समझ में आता है कि यह plpgsql में काम करता है, भी, जैसा कि मुझे उम्मीद है कि इसी तरह के plpgsql मामले काम कर रहे हैं।
इरविन ब्रान्डस्टेट्टर

जवाबों:


10

मैंने इसे pgsql- बग पर पोस्ट किया है और टॉम लेन से वहां का जवाब इंगित करता है कि यह एक लॉक एस्केलेशन मुद्दा है, जिस तरह से एसक्यूएल भाषा के कार्यों को संसाधित किया जाता है, उसके मैकेनिकों द्वारा प्रच्छन्न है। अनिवार्य रूप से, द्वारा उत्पन्न तालाinsert मेज पर विशेष लॉक से पहले प्राप्त :

मेरा मानना ​​है कि इसके साथ समस्या यह है कि एक एसक्यूएल फ़ंक्शन एक ही बार में पूरे फ़ंक्शन बॉडी के लिए पार्सिंग (और शायद योजना भी बना रहा है; अभी कोड की जाँच करने का मन नहीं करता है)। इसका मतलब यह है कि INSERT कमांड के कारण आप फ़ंक्शन बॉडी पार्सिंग के दौरान "टेस्ट" टेबल पर RowExclusiveLock प्राप्त करते हैं, इससे पहले कि LOCK कमांड वास्तव में निष्पादित होता है। इसलिए LOCK एक लॉक एस्केलेशन प्रयास का प्रतिनिधित्व करता है, और गतिरोध की उम्मीद की जाती है।

यह कोडिंग तकनीक plpgsql में सुरक्षित होगी, लेकिन SQL-भाषा फ़ंक्शन में नहीं।

SQL- भाषा फ़ंक्शंस को फिर से लागू करने की चर्चा की गई है ताकि पार्सिंग एक समय में एक बयान हो, लेकिन उस दिशा में कुछ होने के बारे में अपनी सांस न रोकें; यह किसी के लिए उच्च प्राथमिकता की चिंता नहीं लगती है।

सादर, टॉम लेन

यह भी बताता है कि क्यों एक लपेटने वाले plpgsql ब्लॉक में फ़ंक्शन के बाहर तालिका को लॉक करना (जैसा कि @ypercube द्वारा सुझाया गया है ) गतिरोध को रोकता है।


3
ठीक बिंदु: ypercube ने वास्तव में एक फ़ंक्शन के बाहर स्पष्ट लेनदेन में सादे SQL में लॉक का परीक्षण किया , जो कि एक plpgsql ब्लॉक के समान नहीं है ।
इरविन ब्रान्डस्टेट्टर

1
काफी सही है, मेरा बुरा। मुझे लगता है कि हम एक और चीज़ के साथ भ्रमित हो रहे थे जो हमने कोशिश की (जो गतिरोध को रोक नहीं पाया)।
जैक कहते हैं कि topanswers.xyz की कोशिश करें।

4

मान लें कि आप new_customer को कॉल करने से पहले एक और कथन चलाते हैं, और वे एक लॉक प्राप्त करते हैं जो EXCLUSIVE(मूल रूप से, ग्राहक तालिका में किसी भी डेटा संशोधन) के साथ टकराव करता है , स्पष्टीकरण बहुत सरल है।

एक समस्या को एक सरल उदाहरण के साथ पुन: उत्पन्न कर सकते हैं (एक फ़ंक्शन भी शामिल नहीं):

CREATE TABLE test(id INTEGER);

पहला सत्र:

BEGIN;

INSERT INTO test VALUES(1);

दूसरा सत्र

BEGIN;
INSERT INTO test VALUES(1);
LOCK TABLE test IN EXCLUSIVE MODE;

पहला सत्र

LOCK TABLE test IN EXCLUSIVE MODE;

जब पहला सत्र सम्मिलित करता है, तो यह ROW EXCLUSIVEएक मेज पर ताला प्राप्त करता है । इस बीच, सत्र 2 का प्रयास भी ROW EXCLUSIVEताला हो जाता है , और EXCLUSIVEताला प्राप्त करने की कोशिश करता है । किस बिंदु पर इसे 1 सत्र के लिए इंतजार करना पड़ता है, क्योंकि EXCLUSIVEलॉक संघर्ष के साथ है ROW EXCLUSIVE। अंत में, 1 सत्र शार्क को कूदता है और EXCLUSIVEलॉक प्राप्त करने की कोशिश करता है , लेकिन चूंकि ताले क्रम में प्राप्त किए जाते हैं, इसलिए यह दूसरे सत्र के बाद कतार में है। बदले में, यह 1 के लिए इंतजार कर रहा है, एक गतिरोध पैदा कर रहा है:

DETAIL:  Process 28514 waits for ExclusiveLock on relation 58331454 of database 44697822; blocked by process 28084.
Process 28084 waits for ExclusiveLock on relation 58331454 of database 44697822; blocked by process 28514

इस समस्या का समाधान है कि जितनी जल्दी हो सके ताले प्राप्त करना, आमतौर पर लेनदेन में पहली चीज के रूप में। दूसरी ओर, PostgreSQL वर्कलोड को केवल कुछ बहुत ही दुर्लभ मामलों में ताले की आवश्यकता होती है, इसलिए मैं सुझाव दूंगा कि आप जिस तरह से ऊपर उठाते हैं उस पर फिर से विचार करें (इस लेख को देखें http://www.depesz.com/2012/06/10 / Why-is-upsert-so-complex / )।


2
यह सब दिलचस्प है, लेकिन डीबी लॉग में संदेश कुछ इस तरह पढ़ेगा: Process 28514 : select new_customer(decode($1::text, 'hex')); Process 28084 : BEGIN; INSERT INTO test VALUES(1); select new_customer(decode($1::text, 'hex'))जबकि जैक बस मिला: Process 12380: select new_customer(decode($1::text, 'hex')) Process 12379: select new_customer(decode($1::text, 'hex'))- यह दर्शाता है कि फ़ंक्शन कॉल दोनों लेनदेन में पहली कमांड है (जब तक कि मैं कुछ याद नहीं कर रहा हूं)।
इरविन ब्रान्डस्टेट्टर

धन्यवाद, और आप जो कहते हैं, मैं उससे सहमत हूं, लेकिन इस मामले में इसका कारण नहीं प्रतीत होता है। यह उस न्यूनतम न्यूनतम परीक्षण मामले में स्पष्ट है जिसे मैंने प्रश्न में जोड़ा है (जो आप स्वयं आजमा सकते हैं)।
जैक कहते हैं कि topanswers.xyz की कोशिश करें।

2
वास्तव में यह पता चलता है कि आप लॉक एस्केलेशन के बारे में सही थे - हालांकि तंत्र सूक्ष्म है
जैक कहते हैं कि topanswers.xyz की कोशिश करें।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.