2011-08-05 3 views
14

हम एक आवेदन पत्र में कुछ लगातार डेटा पर ब्लॉक "अद्यतन के लिए चुनें", कि एक सर्वर से क्वेरी की जाती है और फिर एक डेटाबेस में संग्रहीत तो हम अतिरिक्त जानकारी का ट्रैक रख सकते। चूंकि हम क्वेरी में नहीं चाहते हैं कि स्मृति में ऑब्जेक्ट का उपयोग कब किया जाता है, हम select for update करते हैं ताकि अन्य थ्रेड जो एक ही डेटा प्राप्त करना चाहते हैं, अवरुद्ध हो जाएंगे।है nonrexisting पंक्तियों

मुझे यकीन है कि कैसे select for update गैर-मौजूद पंक्तियों हैंडल नहीं कर रहा हूँ। यदि पंक्ति मौजूद नहीं है और एक अन्य धागा एक ही पंक्ति पर select for update करने की कोशिश करता है, तो क्या यह थ्रेड अवरुद्ध हो जाएगा जब तक अन्य लेनदेन समाप्त नहीं हो जाता है या क्या इसे खाली परिणाम सेट भी मिल जाएगा? यदि यह केवल खाली परिणाम सेट प्राप्त करता है तो इसे ब्लॉक करने का कोई तरीका भी है, उदाहरण के लिए गायब पंक्ति को तत्काल डालने से?

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

क्योंकि वहाँ एक टिप्पणी थी, कि हम बहुत ज्यादा यहाँ हमारे मामले में ठोस उपयोग पर कुछ और जानकारी लॉक हो सकता है। कम स्यूडोकोड में हमारे programm प्रवाह इस तरह दिखता है:

d = queue.fetch(); 
r = SELECT * FROM table WHERE key = d.key() FOR UPDATE; 
if r.empty() then 
    r = get_data_from_somewhere_else(); 

new_r = process_stuff(r); 


if Data was present then 
    update row to new_r 
else 
    insert new_r 

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

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

अब मैं निम्नलिखित समाधान, जो मेरे लिए एक बदसूरत हैक की तरह लगता है है के लिए।

select the data for update 
if zero rows match then 
    insert some dummy data // this will block if multiple transactions try to insert 
    if insertion failed then 
    // somebody beat us at the race 
    select the data for update 

do processing 

if data was changed then 
    update the old or dummy data 
else 
    rollback the whole transaction 

मैं न तो 100% यकीन है कि हालांकि कि यह वास्तव में समस्या का हल, और न ही इस समाधान अच्छा शैली लगते करता हूँ। तो अगर किसी को कुछ और उपयोग करने योग्य पेशकश करना है तो यह बहुत अच्छा होगा।

+0

आपके संपादन के संबंध में: आपको लॉक कहां मिल रहा है? –

+0

@Catcall: ऊपर, मैं चुनिंदा वक्तव्य में "अद्यतन के लिए" भूल गया। तो यह वह जगह है जहां ताला लगाया जाता है। – LiKao

उत्तर

15

मुझे यकीन नहीं है कि अपडेट के लिए कैसे चयन मौजूदा गैर-मौजूदा पंक्तियों को चुनता है।

ऐसा नहीं है।

सबसे अच्छा आप कर सकते हैं यदि आप कुछ नया पंक्ति के बारे में अद्वितीय पता एक सलाहकार लॉक का उपयोग करने के लिए है। (Hashtext उपयोग करें() यदि आवश्यक हो तो, और तालिका के OID यह लॉक करने के लिए।)

दूसरी सबसे बड़ी चीज एक मेज ताला है।

कहा जा रहा है, आपके सवाल यह ध्वनि की तरह आप आपको चाहिए की तुलना में जिस तरह से अधिक ताला लगा रहे हैं बनाता है। जब आपको वास्तव में आवश्यकता होती है तो पंक्तियों को केवल लॉक करें, यानी ऑपरेशन लिखें।

+0

सलाहकार ताले पर संकेत के लिए धन्यवाद। पहली नजर से यह सहायक लगता है, लेकिन मैं थोड़ा संदिग्ध हूं क्योंकि प्रलेखन कहता है कि सलाहकार ताले लेनदेन से बंधे नहीं हैं, इसलिए हमें उन्हें अनलॉक करना सुनिश्चित करना होगा (यदि प्रोग्राम क्रैश हो तो क्या होगा)। इसके अलावा मुझे नहीं लगता कि हम बहुत अधिक लॉक कर रहे हैं (संपादन देखें)। – LiKao

+0

यह सही है, लेकिन पोस्टग्रेज़ 9.1 की जांच करें: यह लेनदेन-स्तर सलाहकार ताले पेश करता है जो स्वचालित रूप से प्रतिबद्ध या रोलबैक पर जारी किए जाते हैं। –

+0

जानना अच्छा है। इसका मतलब यह है कि जब हम 9.1 पर स्विच करते हैं तो हम बहुत सारे तर्क को कम कर सकते हैं। दुर्भाग्यवश दुर्भाग्य से हम 8.4 :( – LiKao

0

दूसरे संपादन में जोड़े गए कोड को देखते हुए, यह सही दिखता है।

यह एक हैक की तरह लग रही के रूप में, वहाँ कुछ विकल्प है - मूल रूप से यह डेटाबेस के लिए डेटाबेस तर्क ले जाने के बारे में है।

कोई भी पूरे को अद्यतन करने के लिए चुनने के लिए है, यदि मौजूद नहीं है तो फ़ंक्शन में तर्क डालें, और इसके बजाय select get_object(key1,key2,etc) करें।

वैकल्पिक रूप से, आप एक सम्मिलित ट्रिगर कर सकते हैं जो प्रविष्टि जोड़ने के प्रयासों को अनदेखा कर देगा यदि यह पहले से मौजूद है, और अपडेट के लिए चयन करने से पहले बस एक डालें। हालांकि, पहले से ही दूसरे कोड में हस्तक्षेप करने की अधिक संभावना है।

(

+0

पहला सुझाव वास्तव में गलत है: यदि आप अपने छद्म कोड में डालने से पहले नींद का वक्तव्य जोड़ते हैं (जो pg_sleep के रूप में भूमि () एसक्यूएल फ़ंक्शन में), एक त्रुटि (जो भी अद्वितीय है पर डुप्ली कुंजी) होगी। एसक्यूएल स्तर पर अपवाद हैंडलिंग भी लॉक करने में विफल रहेगी, लेकिन त्रुटि नहीं होगी। ओपी का वर्तमान संपादन काम पूरा करेगा, हालांकि –

+0

मैं वास्तव में दूसरे संपादन का जिक्र कर रहा था, "एक हैक जैसा दिखता है"। शायद यह स्पष्ट नहीं था कि आपने इसे निम्नलिखित वाक्य से लिंक नहीं किया है। – MaHuJa

0

उदाहरण समाधान (अगर मैं करने के लिए, मैं संपादित करें और जब मैं एक स्थिति में हूँ कि मैं क्या कर रहा हूँ की जाँच करने पर बाद में उदाहरण कोड जोड़ें। जाएगा याद) मैं बेहतर नहीं पाया है : /)

थ्रेड एक:

BEGIN; 
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID 
SELECT * FROM t WHERE id = 1; 
DELETE FROM t WHERE id = 1; 
INSERT INTO t (id, value) VALUES (1, 'thread A'); 
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation 
COMMIT; 

थ्रेड बी:

BEGIN; 
SELECT pg_advisory_xact_lock(42); -- database semaphore arbitrary ID 
SELECT * FROM t WHERE id = 1; 
DELETE FROM t WHERE id = 1; 
INSERT INTO t (id, value) VALUES (1, 'thread B'); 
SELECT 1 FROM pg_sleep(10); -- only for race condition simulation 
COMMIT; 

हमेशा लेनदेन निष्पादन के सही क्रम का कारण बनता है।

संबंधित मुद्दे