2009-08-25 9 views
8

मैं एक थोक प्रविष्टि-या-अपडेट करने के लिए पोस्टग्रेस बैकएंड के साथ स्क्लेक्लेमी का उपयोग कर रहा हूं। प्रदर्शन में सुधार करने की कोशिश करने के लिए, मैं हर हज़ार पंक्तियों में केवल एक बार प्रतिबद्ध करने का प्रयास कर रहा हूं:मैं SQLQchechemy के साथ एक थोक सम्मिलन-या-अद्यतन कुशलतापूर्वक कैसे करूं?

trans = engine.begin() 
    for i, rec in enumerate(records): 
    if i % 1000 == 0: 
     trans.commit() 
     trans = engine.begin() 
    try: 
     inserter.execute(...) 
    except sa.exceptions.SQLError: 
     my_table.update(...).execute() 
trans.commit() 

हालांकि, यह काम नहीं कर रहा है। ऐसा लगता है कि जब INSERT विफल हो जाता है, तो यह चीजों को एक अजीब स्थिति में छोड़ देता है जो अद्यतन को होने से रोकता है। क्या यह स्वचालित रूप से लेनदेन वापस ले रहा है? यदि हां, तो क्या इसे रोका जा सकता है? मैं नहीं चाहता कि मेरा पूरा लेनदेन किसी समस्या की स्थिति में वापस लुढ़का जाए, यही कारण है कि मैं अपवाद को पहली जगह पकड़ने की कोशिश कर रहा हूं।

त्रुटि संदेश जो मैं प्राप्त कर रहा हूं, बीटीडब्लू, "sqlalchemy.exc.InternalError: (InternalError) वर्तमान लेनदेन निरस्त कर दिया गया है, लेनदेन ब्लॉक के अंत तक अनदेखा आदेश", और यह अद्यतन() पर निष्पादित होता है। निष्पादित करें () कॉल करें।

उत्तर

5

आप कुछ अजीब पोस्टग्रेस्क्ल-विशिष्ट व्यवहार को मार रहे हैं: यदि लेनदेन में कोई त्रुटि होती है, तो यह पूरे लेनदेन को वापस लुढ़कने के लिए मजबूर करता है। मैं इसे एक पोस्टग्रेज़ डिज़ाइन बग मानता हूं; कुछ मामलों में काम करने के लिए एसक्यूएल विरूपणवाद में काफी कुछ लगता है।

एक वर्कअराउंड अद्यतन को पहले करना है। यह पता लगाएं कि क्या यह वास्तव में कर्सर को देखकर एक पंक्ति संशोधित करता है .rowcount; अगर उसने किसी भी पंक्ति को संशोधित नहीं किया है, तो यह अस्तित्व में नहीं था, इसलिए INSERT करें। (यह तेजी से अगर आप अधिक बार की तुलना में आप निश्चित रूप से डालें, अद्यतन किया जाएगा।)

एक और वैकल्पिक हल savepoints उपयोग करने के लिए है:

SAVEPOINT a; 
INSERT INTO ....; 
-- on error: 
ROLLBACK TO SAVEPOINT a; 
UPDATE ...; 
-- on success: 
RELEASE SAVEPOINT a; 

यह उत्पादन गुणवत्ता वाले कोड के लिए एक गंभीर समस्या है: क्या आप करने के लिए है सही त्रुटि का पता लगाएं। संभवतः आप एक अनूठी बाधा जांच को मारने की उम्मीद कर रहे हैं, लेकिन आप अप्रत्याशित कुछ हिट कर सकते हैं, और यह अप्रत्याशित रूप से अपेक्षित त्रुटि को विश्वसनीय रूप से अलग करने के लिए असंभव के बगल में हो सकता है। यदि यह गलती से त्रुटि की स्थिति को हिट करता है, तो यह अस्पष्ट समस्याओं का कारण बन जाएगा जहां कुछ भी अपडेट या डाला नहीं जाएगा और कोई त्रुटि नहीं देखी जाएगी। इसके साथ बहुत सावधान रहें। आप पोस्टग्रेस्क्ल के त्रुटि कोड को देखकर त्रुटि केस को संकुचित कर सकते हैं यह सुनिश्चित करने के लिए कि यह आपके द्वारा अपेक्षित त्रुटि प्रकार है, लेकिन संभावित समस्या अभी भी वहां है।

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

+1

"यदि किसी लेनदेन में कोई त्रुटि होती है, तो यह पूरे लेनदेन को वापस लुढ़कने के लिए मजबूर करता है। मैं इसे पोस्टग्रेज़ डिज़ाइन बग मानता हूं।" - क्या यह लेनदेन का मुद्दा नहीं है? [विकिपीडिया] से (http: //en.wikipedia।संगठन/विकी/डेटाबेस_ट्रांसेक्शन): "लेनदेन एक 'सब-या-कुछ भी नहीं' प्रस्ताव प्रदान करते हैं, यह बताते हुए कि किसी डेटाबेस में किए गए प्रत्येक कार्य-इकाई को या तो पूरी तरह से पूर्ण होना चाहिए या इसका कोई प्रभाव नहीं होना चाहिए।" – spiffytech

+0

@Spiffytech अच्छी प्रतिक्रिया। यह वास्तव में मुझे एलओएल बना दिया। –

4

यह त्रुटि PostgreSQL से है। PostgreSQL आपको एक ही लेनदेन में आदेश निष्पादित करने की अनुमति नहीं देता है यदि एक आदेश एक त्रुटि बनाता है। इसे ठीक करने के लिए आप conn.begin_nested() के माध्यम से नेस्टेड लेनदेन (एसक्यूएल सेवपॉइंट्स का उपयोग करके कार्यान्वित) का उपयोग कर सकते हैं। कुछ ऐसा काम करता है जो काम कर सकता है। मैंने कोड को स्पष्ट कनेक्शन का उपयोग किया, चंकिंग भाग को फैक्टर किया और कोड को सही ढंग से लेनदेन प्रबंधित करने के लिए संदर्भ प्रबंधक का उपयोग किया।

from itertools import chain, islice 
def chunked(seq, chunksize): 
    """Yields items from an iterator in chunks.""" 
    it = iter(seq) 
    while True: 
     yield chain([it.next()], islice(it, chunksize-1)) 

conn = engine.commit() 
for chunk in chunked(records, 1000): 
    with conn.begin(): 
     for rec in chunk: 
      try: 
       with conn.begin_nested(): 
        conn.execute(inserter, ...) 
      except sa.exceptions.SQLError: 
       conn.execute(my_table.update(...)) 

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

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