2013-06-19 8 views
11

के बाद स्मृति को लीक करना मैं psycopg2 (मैं संस्करण 2.5 में अपग्रेड किया गया) का उपयोग कर अपने पोस्टग्रेज़ डेटाबेस के खिलाफ एक पायथन स्क्रिप्ट में एक बड़ी क्वेरी चला रहा हूं। क्वेरी समाप्त होने के बाद, मैं कर्सर और कनेक्शन बंद करता हूं, और यहां तक ​​कि जीसी चलाता हूं, लेकिन प्रक्रिया अभी भी स्मृति की एक टन (7.3 जीबी सटीक होने के लिए) का उपभोग करती है। क्या मुझे क्लीनअप कदम याद आ रहा है?psycopg2 बड़ी क्वेरी

import psycopg2 
conn = psycopg2.connect("dbname='dbname' user='user' host='host'") 
cursor = conn.cursor() 
cursor.execute("""large query""") 
rows = cursor.fetchall() 
del rows 
cursor.close() 
conn.close() 
import gc 
gc.collect() 

उत्तर

8

कृपया बेहतर समाधान के लिए @joeblog द्वारा अगले जवाब देखते हैं।


सबसे पहले, आपको पहले स्थान पर सभी रैम की आवश्यकता नहीं होनी चाहिए। आपको यहां क्या करना चाहिए, परिणाम सेट के भाग ला रहा है। fetchall() मत करो। इसके बजाय, अधिक कुशल cursor.fetchmany विधि का उपयोग करें। the psycopg2 documentation देखें।

अब, स्पष्टीकरण क्यों है कि इसे मुक्त नहीं किया गया है, और यह उस शब्द के औपचारिक रूप से सही उपयोग में स्मृति रिसाव क्यों नहीं है।

अधिकांश प्रक्रियाएं ओएस को वापस मुक्त होने पर स्मृति को रिलीज़ नहीं करती हैं, वे इसे प्रोग्राम में कहीं और फिर से उपयोग के लिए उपलब्ध कराते हैं।

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

प्रोग्राम द्वारा अतिरिक्त सावधानी बरतने तक आम तौर पर तब तक क्या हो रहा है जब brk() के साथ आवंटित स्मृति का प्रत्येक बड़ा हिस्सा अभी भी उपयोग में आने वाले कुछ छोटे टुकड़ों के साथ उतरा है।

ओएस यह नहीं बता सकता कि प्रोग्राम इस स्मृति को अभी भी उपयोग में रखता है या नहीं, इसलिए यह केवल इसका दावा नहीं कर सकता है। चूंकि प्रोग्राम मेमोरी तक नहीं पहुंचता है, इसलिए ओएस आमतौर पर समय के साथ इसे स्वैप कर देगा, अन्य उपयोगों के लिए भौतिक स्मृति मुक्त कर देगा। स्वैप स्पेस होने के कारणों में से एक यह है।

प्रोग्राम लिखना संभव है जो स्मृति को ओएस पर वापस रखता है, लेकिन मुझे यकीन नहीं है कि आप इसे पायथन के साथ कर सकते हैं।

यह भी देखें:

तो: यह नहीं वास्तव में एक स्मृति रिसाव है। यदि आप कुछ और काम करते हैं जो बहुत सारी मेमोरी का उपयोग करता है, तो प्रक्रिया को बहुत अधिक नहीं बढ़ना चाहिए, यह पिछले बड़े आवंटन से पूर्व मुक्त स्मृति का पुन: उपयोग करेगा।

+0

धन्यवाद! पुष्टि की है कि उपरोक्त कोड को एक ही प्रक्रिया में दो बार चलाकर स्मृति का पुन: उपयोग किया जाता है। दूसरे रन के दौरान मेमोरी में वृद्धि नहीं हुई। –

+3

जबकि सबकुछ यहां कहा गया है, एक सामान्य परिणाम सामान्य रूप से ग्राहक पक्ष पर स्थानांतरित किया जाएगा ('fetch *() 'द्वारा नहीं बल्कि' execute() ')। तो 'fetchall() 'के बजाय' fetchmany() 'का उपयोग करते समय पाइथन ऑब्जेक्ट सृजन के मामले में कुछ मेमोरी सहेज सकते हैं, सर्वर-साइड कर्सर का उपयोग करके @joeblog द्वारा सुझाया गया सही समाधान है। – piro

27

मैं इसी तरह की समस्या में भाग गया और रक्त, पसीने और आंसुओं के कुछ घंटों के बाद, जवाब में पाया गया कि केवल एक पैरामीटर को जोड़ने की आवश्यकता है।

cursor = conn.cursor() 

लिखने

cursor = conn.cursor(name="my_cursor_name") 

या सरल अभी तक

cursor = conn.cursor("my_cursor_name") 

विवरण http://initd.org/psycopg/docs/usage.html#server-side-cursors

पर पाए जाते हैं के बजाय

मैं अनुदेश पाया उसमें थोड़ा उलझन में है, हालांकि मुझे "DECLARE my_cursor_name ...." और फिर "my_cursor_name से FETCH गिनती 2000" शामिल करने के लिए मेरे एसक्यूएल को फिर से लिखना होगा, लेकिन यह पता चला है कि psycopg यह सब आपके लिए हुड के नीचे करता है कर्सर बनाते समय आप बस "name = none" डिफ़ॉल्ट पैरामीटर को ओवरराइट करते हैं।

fetchone या fetchmany का उपयोग करने के ऊपर दिए गए सुझाव से समस्या का समाधान नहीं होता है, यदि आप नाम पैरामीटर को अनसेट करते हैं, तो psycopg पूरी क्वेरी को राम में लोड करने का डिफ़ॉल्ट प्रयास करेगा। एकमात्र अन्य चीज जिसे आपको (नाम पैरामीटर घोषित करने के अलावा) को कर्सर.इटरसाइज एट्रिब्यूट को डिफ़ॉल्ट 2000 से 1000 कहने के लिए बदलना है, यदि आपके पास अभी भी बहुत कम स्मृति है।

+0

मुझे 'sqlalchemy' में कुछ भी नहीं मिला जो मुझे ओओएम मुद्दे से बचने में मदद करता था, लेकिन यह समाधान मेरे लिए काम करता था। धन्यवाद! –

7

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

import datetime as dt 
import psycopg2 
import sys 
import time 

conPG = psycopg2.connect("dbname='myDearDB'") 
curPG = conPG.cursor('testCursor') 
curPG.itersize = 100000 # Rows fetched at one time from the server 

curPG.execute("SELECT * FROM myBigTable LIMIT 10000000") 
# Warning: curPG.rowcount == -1 ALWAYS !! 
cptLigne = 0 
for rec in curPG: 
    cptLigne += 1 
    if cptLigne % 10000 == 0: 
     print('.', end='') 
     sys.stdout.flush() # To see the progression 
conPG.commit() # Also close the cursor 
conPG.close() 

आप देखेंगे के रूप में, डॉट्स समूह द्वारा तेजी से करने, रोकने से पंक्तियों की एक बफर प्राप्त करने के लिए आया था (itersize), आप प्रदर्शन के लिए fetchmany उपयोग करने के लिए की जरूरत नहीं है तो। जब मैं इसे /usr/bin/time -v के साथ चलाता हूं, तो मुझे 10 मिलियन पंक्तियों के लिए केवल 200 एमबी रैम (क्लाइंट-साइड कर्सर के साथ 60 जीबी की बजाय) का उपयोग करके 3 मिनट से भी कम समय में परिणाम मिलता है। सर्वर को अधिक रैम की आवश्यकता नहीं है क्योंकि यह अस्थायी तालिका का उपयोग करता है।

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