2012-01-03 13 views
5

मैं डेटाबेस में सभी पंक्तियों पर एक बैच ऑपरेशन चला रहा हूं। इसमें प्रत्येक मॉडल का चयन करना और इसमें कुछ करना शामिल है। इसे टुकड़ों में विभाजित करना और इसे खंड से अलग करना समझ में आता है।Django QuerySet के साथ भागों में डेटाबेस को संसाधित करने का सबसे अच्छा तरीका?

मैं वर्तमान में पेजिनेटर का उपयोग कर रहा हूं, क्योंकि यह सुविधाजनक है। इसका मतलब है कि मुझे मूल्यों पर ऑर्डर करने की आवश्यकता है ताकि उन्हें क्रम में पग किया जा सके। यह SQL स्टेटमेंट जेनरेट करता है जिसमें order और limit क्लॉज हैं, और प्रत्येक खंड के लिए मुझे लगता है कि पोस्टग्रेस पूरी तालिका को सॉर्ट कर सकता है (हालांकि मैं आंतरिक के बारे में कोई जानकारी नहीं ले सकता)। मुझे पता है कि डेटाबेस लगभग 50% सीपीयू पर है और मुझे लगता है कि select एस करने के लिए यह बहुत अधिक है।

आरडीएमबीएस/सीपीयू-अनुकूल तरीके से पूरी तालिका में फिर से शुरू करने का सबसे अच्छा तरीका क्या है?

यह मानते हुए कि डेटाबेस की सामग्री बैच ऑपरेशन के दौरान नहीं बदली जाती है।

उत्तर

5

आपके विवरण से आप वास्तव में सॉर्ट ऑर्डर की प्रक्रियाओं की परवाह नहीं करते हैं।

SELECT * FROM tbl WHERE id BETWEEN 0 AND 1000; 
SELECT * FROM tbl WHERE id BETWEEN 1001 AND 2000; 
... 

यह किसी भी ऑफसेट और (लगभग) किसी भी आकार के लिए एक ही के लिए एक ही करता है: आप अपनी तालिका में प्राथमिक कुंजी (! जो मैं उम्मीद), विभाजन के इस कच्चे विधि बहुत तेजी से होगा है टेबल का तदनुसार मिनट और अपने प्राथमिक कुंजी और विभाजन की अधिकतम प्राप्त करें:

SELECT min(id), max(id) from tbl; -- then divide in suitable chunks 

के रूप में करने का विरोध किया:

SELECT * FROM tbl ORDER BY id LIMIT 1000; 
SELECT * FROM tbl ORDER BY id LIMIT 1000 OFFSET 1000; 
... 

यह आम तौर पर धीमी है क्योंकि सभी पंक्तियों क्रमबद्ध करना है और प्रदर्शन के साथ अतिरिक्त खराब हो उच्च ऑफसेट और बड़ी टेबल।

एक उपयोगिता समारोह है कि एक मनमाना Django क्वेरीसमूह लिए यह कर देगा इस प्रकार है:

+0

मतलब यह है कि रिकॉर्ड एक 'sort' खंड के बिना एक ही क्रम में लौटा दिया जाता है। क्या ये सही है? साथ ही, अगर मेरे पास 'मेटा' कक्षा में डिफ़ॉल्ट सॉर्टिंग है तो क्या मैं इसे क्वेरी के लिए किसी भी तरह से हटा सकता हूं? – Joe

+0

@ जो: मूल रूप से आपको एक ही रिकॉर्ड मिलते हैं, लेकिन बिना छेड़छाड़ की जाती है। यदि आपके आईडी-स्पेस में अंतराल हैं, तो रिकॉर्ड्स की संख्या प्रत्येक कॉल के लिए अपेक्षा से कम हो सकती है। जबकि LIMIT/OFFSET के साथ आपको क्रमबद्ध पंक्तियों की एक निश्चित संख्या मिलती है (प्रति तालिका अंतिम कॉल को छोड़कर)। मैं 'मेटा' वर्ग को संभालने का तरीका नहीं हूं, लेकिन आपको अपनी पंक्तियों को LIMIT/OFFSET के लिए ऑर्डर करने की आवश्यकता है। –

+0

इरविन, मुझे सच में खेद है कि मैंने आपका जवाब सही ढंग से नहीं पढ़ा। क्या आप वाकई तेज़ हैं? 'बीच' खंड निश्चित रूप से केवल तभी काम कर सकता है यदि आईडी पहले से ही क्रमबद्ध हो, या यह हर बार एक संपूर्ण टेबल स्कैन करता है? – Joe

2

निम्नलिखित कोड ऊपर (BETWEEN का प्रयोग करके) एक Django क्वेरीसमूह के लिए इरविन के जवाब लागू करता है। यह 'आईडी' मानने के लिए डिफ़ॉल्ट है between खंड के लिए उपयोग करने के लिए एक उपयुक्त फ़ील्ड है।

def chunked_queryset(qs, batch_size, index='id'): 
    """ 
    Yields a queryset split into batches of maximum size 'batch_size'. 
    Any ordering on the queryset is discarded. 
    """ 
    qs = qs.order_by() # clear ordering 
    min_max = qs.aggregate(min=models.Min(index), max=models.Max(index)) 
    min_id, max_id = min_max['min'], min_max['max'] 
    for i in range(min_id, max_id + 1, batch_size): 
     filter_args = {'{0}__range'.format(index): (i, i + batch_size - 1)} 
     yield qs.filter(**filter_args) 

यह इस तरह इस्तेमाल किया जाएगा:

for chunk in chunked_queryset(SomeModel.objects.all(), 20): 
    # `chunk` is a queryset 
    for item in chunk: 
     # `item` is a SomeModel instance 
     pass 
संबंधित मुद्दे

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