ORA-01000, अधिकतम-ओपन-कर्सर त्रुटि, Oracle डेटाबेस विकास में एक अत्यंत सामान्य त्रुटि है। जावा के संदर्भ में, ऐसा तब होता है जब एप्लिकेशन डेटाबेस इंस्टेंस पर कॉन्फ़िगर किए गए कर्सर की तुलना में अधिक परिणामसेट खोलने का प्रयास करता है।
सामान्य कारण हैं:
-
कॉन्फ़िगरेशन त्रुटि
- डीबी पर कर्सर की तुलना में डेटाबेस से पूछताछ करने वाले आपके एप्लिकेशन में आपके पास अधिक धागे हैं। एक मामला यह है कि आपके पास कनेक्शन और थ्रेड पूल डेटाबेस पर कर्सर की संख्या से बड़ा है।
- आपके पास एक ही डीबी इंस्टेंस से जुड़े कई डेवलपर या एप्लिकेशन हैं (जिसमें शायद कई स्कीमा शामिल होंगे) और साथ में आप बहुत सारे कनेक्शन का उपयोग कर रहे हैं।
-
समाधान:
- डेटाबेस पर कर्सर की संख्या बढ़ाना (यदि संसाधन अनुमति देते हैं) या
- एप्लिकेशन में थ्रेड्स की संख्या घटाना।
-
कर्सर लीक
- एप्लिकेशन परिणामसेट (जेडीबीसी में) या कर्सर (डेटाबेस पर संग्रहीत प्रक्रियाओं में) को बंद नहीं कर रहे हैं
- समाधान :कर्सर लीक बग हैं; डीबी पर कर्सर की संख्या बढ़ाना अनिवार्य विफलता में देरी करता है। स्थिर कोड विश्लेषण, JDBC या एप्लिकेशन-लेवल लॉगिंग और डेटाबेस मॉनिटरिंग का उपयोग करके लीक का पता लगाया जा सकता है।
पृष्ठभूमि
यह खंड कर्सर के पीछे के कुछ सिद्धांतों का वर्णन करता है और कैसे JDBC का उपयोग किया जाना चाहिए। यदि आपको पृष्ठभूमि जानने की आवश्यकता नहीं है, तो आप इसे छोड़ सकते हैं और सीधे 'लीक खत्म करना' पर जा सकते हैं।
कर्सर क्या है?
एक कर्सर डेटाबेस पर एक संसाधन है जो एक क्वेरी की स्थिति रखता है, विशेष रूप से वह स्थिति जहां एक पाठक परिणामसेट में होता है। प्रत्येक SELECT स्टेटमेंट में एक कर्सर होता है, और PL/SQL संग्रहीत कार्यविधियाँ जितनी चाहें उतने कर्सर खोल और उपयोग कर सकती हैं। आप ओराफ़ाक पर कर्सर के बारे में अधिक जानकारी प्राप्त कर सकते हैं।
एक डेटाबेस इंस्टेंस आमतौर पर कई अलग-अलग स्कीमा परोसता है , कई अलग-अलग उपयोगकर्ता प्रत्येक के साथ एकाधिक सत्र . ऐसा करने के लिए, इसमें सभी स्कीमा, उपयोगकर्ताओं और सत्रों के लिए निश्चित संख्या में कर्सर उपलब्ध हैं। जब सभी कर्सर खुले होते हैं (उपयोग में) और अनुरोध आता है जिसमें एक नए कर्सर की आवश्यकता होती है, तो अनुरोध ORA-010000 त्रुटि के साथ विफल हो जाता है।
कर्सर की संख्या खोजना और सेट करना
संख्या सामान्य रूप से स्थापना पर डीबीए द्वारा कॉन्फ़िगर की जाती है। वर्तमान में उपयोग में आने वाले कर्सर की संख्या, अधिकतम संख्या और कॉन्फ़िगरेशन को Oracle SQL डेवलपर में व्यवस्थापक कार्यों में एक्सेस किया जा सकता है। SQL से इसे इसके साथ सेट किया जा सकता है:
ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
जेवीएम में जेडीबीसी को डीबी पर कर्सर से जोड़ना
नीचे दिए गए JDBC ऑब्जेक्ट को निम्नलिखित डेटाबेस अवधारणाओं के साथ कसकर जोड़ा गया है:
- JDBC कनेक्शन एक डेटाबेस का क्लाइंट प्रतिनिधित्व है सत्र और डेटाबेस लेनदेन प्रदान करता है . एक कनेक्शन में किसी एक समय में केवल एक ही लेन-देन खुला हो सकता है (लेकिन लेन-देन नेस्टेड किया जा सकता है)
- एक JDBC परिणाम सेट एकल कर्सर . द्वारा समर्थित है डेटाबेस पर। जब परिणामसेट पर क्लोज़ () को कॉल किया जाता है, तो कर्सर रिलीज़ हो जाता है।
- एक JDBC कॉल करने योग्य विवरण एक संग्रहीत प्रक्रिया . का आह्वान करता है डेटाबेस पर, अक्सर PL/SQL में लिखा जाता है। संग्रहीत कार्यविधि शून्य या अधिक कर्सर बना सकती है, और एक कर्सर को JDBC परिणामसेट के रूप में वापस कर सकती है।
JDBC थ्रेड सुरक्षित है:थ्रेड्स के बीच विभिन्न JDBC ऑब्जेक्ट्स को पास करना काफी ठीक है।
उदाहरण के लिए, आप एक थ्रेड में कनेक्शन बना सकते हैं; एक अन्य थ्रेड इस कनेक्शन का उपयोग रेडीस्टेडमेंट बनाने के लिए कर सकता है और तीसरा थ्रेड परिणाम सेट को संसाधित कर सकता है। एकमात्र प्रमुख प्रतिबंध यह है कि आप किसी भी समय एक से अधिक परिणामसेट एक ही तैयार स्टेटमेंट पर नहीं खोल सकते हैं। देखें क्या Oracle DB प्रति कनेक्शन एकाधिक (समानांतर) संचालन का समर्थन करता है?
ध्यान दें कि एक कनेक्शन पर एक डेटाबेस कमिट होता है, और इसलिए उस कनेक्शन पर सभी DML (INSERT, UPDATE और DELETE's) एक साथ कमिट करेंगे। इसलिए, यदि आप एक ही समय में कई लेन-देन का समर्थन करना चाहते हैं, तो आपके पास प्रत्येक समवर्ती लेनदेन के लिए कम से कम एक कनेक्शन होना चाहिए।
JDBC ऑब्जेक्ट को बंद करना
ResultSet को क्रियान्वित करने का एक विशिष्ट उदाहरण है:
Statement stmt = conn.createStatement();
try {
ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
try {
while ( rs.next() ) {
System.out.println( "Name: " + rs.getString("FULL_NAME") );
}
} finally {
try { rs.close(); } catch (Exception ignore) { }
}
} finally {
try { stmt.close(); } catch (Exception ignore) { }
}
ध्यान दें कि अंत में क्लॉज क्लोज () द्वारा उठाए गए किसी भी अपवाद को कैसे नजरअंदाज करता है:
- यदि आप {} कैच {} के बिना रिजल्टसेट को आसानी से बंद कर देते हैं, तो यह विफल हो सकता है और स्टेटमेंट को बंद होने से रोक सकता है
- हम कॉल करने वाले को प्रोपेगेट करने की कोशिश के शरीर में उठाए गए किसी भी अपवाद को अनुमति देना चाहते हैं। यदि आपके पास लूप ओवर है, उदाहरण के लिए, स्टेटमेंट बनाना और निष्पादित करना, लूप के भीतर प्रत्येक स्टेटमेंट को बंद करना याद रखें।
Java 7 में, Oracle ने AutoCloseable इंटरफ़ेस पेश किया है जो अधिकांश Java 6 बॉयलरप्लेट को कुछ अच्छी वाक्यात्मक चीनी के साथ बदल देता है।
JDBC ऑब्जेक्ट को होल्ड करना
JDBC ऑब्जेक्ट को स्थानीय चर, ऑब्जेक्ट इंस्टेंस और क्लास के सदस्यों में सुरक्षित रूप से रखा जा सकता है। यह आम तौर पर बेहतर अभ्यास है:
- JDBC ऑब्जेक्ट्स को रखने के लिए ऑब्जेक्ट इंस्टेंस या क्लास के सदस्यों का उपयोग करें, जिनका लंबी अवधि में कई बार पुन:उपयोग किया जाता है, जैसे कि कनेक्शन और तैयार स्टेटमेंट
- ResultSets के लिए स्थानीय चर का उपयोग करें क्योंकि ये प्राप्त किए जाते हैं, लूप किए जाते हैं और फिर आमतौर पर एक फ़ंक्शन के दायरे में बंद हो जाते हैं।
हालांकि, एक अपवाद है:यदि आप ईजेबी, या सर्वलेट/जेएसपी कंटेनर का उपयोग कर रहे हैं, तो आपको सख्त थ्रेडिंग मॉडल का पालन करना होगा:
- केवल एप्लिकेशन सर्वर थ्रेड बनाता है (जिसके साथ यह आने वाले अनुरोधों को संभालता है)
- केवल एप्लिकेशन सर्वर कनेक्शन बनाता है (जो आप कनेक्शन पूल से प्राप्त करते हैं)
- कॉल के बीच वैल्यू (स्टेट) को सेव करते समय, आपको बहुत सावधान रहने की जरूरत है। अपने स्वयं के कैश या स्थिर सदस्यों में मूल्यों को कभी भी स्टोर न करें - यह क्लस्टर और अन्य अजीब स्थितियों में सुरक्षित नहीं है, और एप्लिकेशन सर्वर आपके डेटा के लिए भयानक चीजें कर सकता है। इसके बजाय स्टेटफुल बीन्स या डेटाबेस का उपयोग करें।
- विशेष रूप से, कभी नहीं JDBC ऑब्जेक्ट्स (कनेक्शन, रिजल्टसेट, रेडीस्टेडमेंट, आदि) को अलग-अलग रिमोट इनवोकेशन पर रखें - एप्लिकेशन सर्वर को इसे प्रबंधित करने दें। एप्लिकेशन सर्वर न केवल एक कनेक्शन पूल प्रदान करता है, बल्कि यह आपके तैयार स्टेटमेंट को भी कैश करता है।
लीक खत्म करना
JDBC लीक का पता लगाने और उसे खत्म करने में मदद करने के लिए कई प्रक्रियाएँ और उपकरण उपलब्ध हैं:
-
विकास के दौरान - बग को जल्दी पकड़ना अब तक का सबसे अच्छा तरीका है:
-
विकास अभ्यास:अच्छे विकास अभ्यासों से आपके सॉफ़्टवेयर के डेवलपर के डेस्क से बाहर निकलने से पहले उसमें बग्स की संख्या कम हो जानी चाहिए। विशिष्ट प्रथाओं में शामिल हैं:
- जोड़ी प्रोग्रामिंग, बिना पर्याप्त अनुभव वालों को शिक्षित करने के लिए
- कोड समीक्षाएं क्योंकि कई आंखें एक से बेहतर होती हैं
- इकाई परीक्षण जिसका अर्थ है कि आप एक परीक्षण उपकरण से अपने किसी भी और सभी कोड आधार का प्रयोग कर सकते हैं जो पुनरुत्पादन लीक को तुच्छ बनाता है
- अपने स्वयं के निर्माण के बजाय कनेक्शन पूलिंग के लिए मौजूदा पुस्तकालयों का उपयोग करें
-
स्टेटिक कोड विश्लेषण:स्थिर कोड विश्लेषण करने के लिए उत्कृष्ट Findbugs जैसे टूल का उपयोग करें। यह कई जगहों को उठाता है जहां बंद() को सही ढंग से संभाला नहीं गया है। फाइंडबग्स में एक्लिप्स के लिए एक प्लगइन है, लेकिन यह एक बार के लिए स्टैंडअलोन भी चलाता है, जेनकिंस सीआई और अन्य बिल्ड टूल्स में एकीकरण है
-
-
रनटाइम पर:
-
धारणीयता और प्रतिबद्धता
- यदि परिणामसेट धारण क्षमता परिणामसेट है।CLOSE_CURSORS_OVER_COMMIT, तो जब Connection.commit() विधि को कॉल किया जाता है तो परिणामसेट बंद हो जाता है। इसे Connection.setHoldability() या ओवरलोडेड Connection.createStatement() विधि का उपयोग करके सेट किया जा सकता है।
-
रनटाइम पर लॉगिंग।
- अपने कोड में अच्छे लॉग स्टेटमेंट डालें। ये स्पष्ट और समझने योग्य होने चाहिए ताकि ग्राहक, सहयोगी स्टाफ और टीम के साथी बिना प्रशिक्षण के समझ सकें। उन्हें संक्षिप्त होना चाहिए और मुख्य चर और विशेषताओं के राज्य/आंतरिक मूल्यों को प्रिंट करना शामिल करना चाहिए ताकि आप प्रसंस्करण तर्क का पता लगा सकें। अच्छा लॉगिंग डिबगिंग अनुप्रयोगों के लिए मौलिक है, विशेष रूप से वे जिन्हें तैनात किया गया है।
-
आप अपने प्रोजेक्ट में एक डिबगिंग JDBC ड्राइवर जोड़ सकते हैं (डिबगिंग के लिए - वास्तव में इसे तैनात न करें)। एक उदाहरण (मैंने इसका उपयोग नहीं किया है) log4jdbc है। फिर आपको यह देखने के लिए इस फ़ाइल पर कुछ सरल विश्लेषण करने की आवश्यकता है कि कौन से निष्पादन में संबंधित बंद नहीं है। संभावित समस्या होने पर खुले और बंद की गिनती को हाइलाइट करना चाहिए
- डेटाबेस की निगरानी करना। SQL डेवलपर 'मॉनीटर SQL' फ़ंक्शन या क्वेस्ट के TOAD जैसे टूल का उपयोग करके अपने चल रहे एप्लिकेशन की निगरानी करें। इस आलेख में निगरानी का वर्णन किया गया है। निगरानी के दौरान, आप खुले कर्सर को क्वेरी करते हैं (उदाहरण के लिए तालिका v$sesstat से) और उनके SQL की समीक्षा करें। यदि कर्सर की संख्या बढ़ रही है, और (सबसे महत्वपूर्ण) एक समान SQL कथन पर हावी हो रहा है, तो आप जानते हैं कि आपके पास उस SQL के साथ एक रिसाव है। अपना कोड खोजें और समीक्षा करें।
-
अन्य विचार
क्या आप कनेक्शन बंद करने के लिए WeakReferences का उपयोग कर सकते हैं?
कमजोर और नरम संदर्भ आपको किसी वस्तु को इस तरह से संदर्भित करने की अनुमति देने के तरीके हैं जो JVM को किसी भी समय उचित समझे जाने वाले संदर्भ को इकट्ठा करने की अनुमति देता है (यह मानते हुए कि उस वस्तु के लिए कोई मजबूत संदर्भ श्रृंखला नहीं है)।
यदि आप कंस्ट्रक्टर में एक रेफरेंस क्यू को सॉफ्ट या कमजोर रेफरेंस में पास करते हैं, तो ऑब्जेक्ट को रेफरेंस क्यू में रखा जाता है जब ऑब्जेक्ट होता है जब यह होता है (यदि यह बिल्कुल होता है)। इस दृष्टिकोण के साथ, आप वस्तु को अंतिम रूप देने के साथ बातचीत कर सकते हैं और उस समय आप वस्तु को बंद या अंतिम रूप दे सकते हैं।
प्रेत संदर्भ थोड़े अजीब हैं; उनका उद्देश्य केवल अंतिम रूप देने को नियंत्रित करना है, लेकिन आप कभी भी मूल वस्तु का संदर्भ प्राप्त नहीं कर सकते हैं, इसलिए उस पर बंद () विधि को कॉल करना कठिन होगा।
हालांकि, जब जीसी चलाया जाता है तो इसे नियंत्रित करने का प्रयास करना शायद ही कभी एक अच्छा विचार है (कमजोर, नरम और प्रेत संदर्भ आपको तथ्य के बाद बताते हैं। कि वस्तु जीसी के लिए कतारबद्ध है)। वास्तव में, यदि JVM में मेमोरी की मात्रा बड़ी है (जैसे -Xmx2000m) तो आप कभी नहीं कर सकते हैं GC ऑब्जेक्ट, और आप अभी भी ORA-01000 का अनुभव करेंगे। यदि JVM मेमोरी आपके प्रोग्राम की आवश्यकताओं के सापेक्ष छोटी है, तो आप पा सकते हैं कि ResultSet और ReadyedStatement ऑब्जेक्ट निर्माण के तुरंत बाद GCed हैं (इससे पहले कि आप उनसे पढ़ सकें), जो संभवतः आपके प्रोग्राम को विफल कर देगा।
टीएल; डॉ: कमजोर संदर्भ तंत्र Statement और ResultSet ऑब्जेक्ट्स को प्रबंधित और बंद करने का एक अच्छा तरीका नहीं है।