2010-02-25 3 views
19

मैंने आज एक घंटे से अधिक समय बिताया एक प्रश्न योजना पर खुद को परेशान कर रहा था जिसे मैं समझ नहीं पाया। क्वेरी UDPATE थी और यह बस सभी को नहीं चलाएगी। पूरी तरह से deadlocked: pg_locks दिखाया कि यह किसी भी चीज़ के लिए इंतजार नहीं कर रहा था। अब, मैं खुद को सबसे अच्छा या सबसे खराब क्वेरी प्लान रीडर लड़का नहीं मानता, लेकिन मुझे यह एक असाधारण मुश्किल लगता है। मैं सोच रहा हूं कोई इन्हें कैसे पढ़ता है? क्या कोई ऐसी पद्धति है जो त्रुटि को इंगित करने के लिए पीजी एसेस का पालन करती है?PostgreSQL क्वेरी योजना पढ़ने पर मैं "बेहतर सोच सकता हूं" कैसे? (उदाहरण संलग्न)

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

          QUERY PLAN           
-------------------------------------------------------------------------------------------- 
Nested Loop Anti Join (cost=47680.88..169413.12 rows=1 width=77) 
    Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name)) 
    -> Nested Loop (cost=5301.58..31738.10 rows=1 width=81) 
     -> Hash Join (cost=5301.58..29722.32 rows=229 width=40) 
       Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text)) 
       -> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36) 
        Filter: (name IS NULL) 
       -> Hash (cost=4547.33..4547.33 rows=36150 width=24) 
        -> Seq Scan on vehicles iv (cost=0.00..4547.33 rows=36150 width=24) 
          Filter: (date_sold IS NULL) 
     -> Index Scan using options_pkey on options co (cost=0.00..8.79 rows=1 width=49) 
       Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code)) 
    -> Hash Join (cost=42379.30..137424.09 rows=16729 width=26) 
     Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text)) 
     -> Seq Scan on vehicles v (cost=0.00..4547.33 rows=65233 width=24) 
     -> Hash (cost=20223.32..20223.32 rows=931332 width=44) 
       -> Seq Scan on options o (cost=0.00..20223.32 rows=931332 width=44) 
(17 rows) 

इस क्वेरी योजना के साथ इस मुद्दे - मेरा मानना ​​है कि मैं समझता हूँ - शायद सबसे अच्छा RhodiumToad द्वारा कहा जाता है (वह निश्चित रूप से इस पर बेहतर है, इसलिए मैं अपने विवरण में बेहतर होने पर दांव लगाया जाएगा) irc://irc.freenode.net/#postgresql की:

ओह, कि योजना संभावित विनाशकारी है कि योजना के साथ समस्या यह प्रत्येक पंक्ति के लिए एक बेहद महंगा hashjoin चल रहा है वह यह है कि समस्या पंक्तियों = 1 दूसरे से अनुमान में शामिल होने और योजनाकार सोचता है कि यह करने के लिए ठीक है एक विशाल रखो एक nestloop के भीतरी पथ में महंगा महंगी क्वेरी जहां बाहरी पथ केवल एक पंक्ति वापस करने का अनुमान है। , जाहिर है, योजनाकार के अनुमान से महंगा हिस्सा केवल पर चलाया जाएगा, लेकिन यह वास्तव में अभ्यास में गड़बड़ करने की स्पष्ट प्रवृत्ति है, समस्या यह है कि योजनाकार का मानना ​​है कि इसका अनुमान आदर्श है, योजनाकार को यह जानने की आवश्यकता है के बीच का अंतर और "संभव नहीं 1 से अधिक पंक्ति लौटने के लिए" "1 पंक्ति लौटने का अनुमान" लेकिन यह बिल्कुल स्पष्ट है कि कैसे मौजूदा कोड

वह आगे कहते हैं कि शामिल करने के लिए नहीं है:

यह किसी भी शामिल होने को प्रभावित कर सकता है, लेकिन आमतौर पर इसके खिलाफ जुड़ता है सबक्वेरी अब सबसे अधिक संभावना

हैं जब मैं इस योजना पहली बात मैंने देखा Nested Loop Anti Join था पढ़ा है, यह (मैं ऊपरी सीमा को रह सकते हैं) 169,413 की लागत से किया था। यह एंटी-जॉइन Nested Loop के परिणामस्वरूप 31,738 की लागत पर के परिणाम पर Hash Join के परिणामस्वरूप टूट गया है। अब, 137,424, 31,738 से अधिक है इसलिए मुझे पता था कि समस्या हैश जॉइन थी। फिर मैं प्रश्न के बाहर हैश जॉइन सेगमेंट EXPLAIN ANALYZE पर आगे बढ़ता हूं। यह 7 सेकंड में निष्पादित किया गया। मैंने सुनिश्चित किया कि इंडेक्स (lot_id, vin), और (co.code, और v.code) पर था - वहां था। मैंने seq_scan और hashjoin को व्यक्तिगत रूप से अक्षम कर दिया और 2 सेकंड से भी कम की गति वृद्धि देखी। इस बात के लिए पर्याप्त नहीं है कि यह एक घंटे के बाद क्यों प्रगति नहीं कर रहा था।

लेकिन, इसके बाद मैं पूरी तरह गलत हूं! हां, यह क्वेरी का धीमा हिस्सा था, लेकिन rows="1" बिट (मुझे लगता है कि यह Nested Loop Anti Join पर था)। क्या कोई ट्यूटोरियल है जो मुझे इस तरह के मुद्दों की पहचान करने में मदद करेगा। यहां योजनाकार में पंक्तियों की मात्रा का गलत अनुमान लगाने की क्षमता में एक बग (क्षमता की कमी) है?RhodiumToad ने उसी निष्कर्ष पर आने के लिए इसे कैसे पढ़ा है?

क्या यह बस rows="1" है जो मुझे यह पता लगाने के लिए ट्रिगर करने वाला है?

मैंने शामिल सभी तालिकाओं पर VACUUM FULL ANALYZE चलाया, और यह पोस्टग्रेस्क्ल 8.4 है।

उत्तर

24

मुद्दों के माध्यम से देखकर यह कोई बात बिगड़ जाए सकते हैं, जहां पर कुछ अनुभव की आवश्यकता है की तरह। लेकिन क्वेरी योजनाओं में मुद्दों को ढूंढने के लिए, उत्पादित योजना को अंदरूनी से सत्यापित करने का प्रयास करें, जांच करें कि पंक्तियों का अनुमान अनुमानित है या लागत अनुमानित समय से मेल खाते हैं। Btw। दो लागत अनुमान कम और ऊपरी सीमाएं नहीं हैं, पहली बार आउटपुट की पहली पंक्ति का उत्पादन करने के लिए अनुमानित लागत है, दूसरा नंबर अनुमानित कुल लागत है, explain documentation विवरण के लिए, planner documentation भी उपलब्ध है। यह यह जानने में भी मदद करता है कि विभिन्न एक्सेस विधियां कैसे काम करती हैं। शुरुआती बिंदु के रूप में विकिपीडिया में nested loop, hash और merge joins पर जानकारी है।

अपने उदाहरण में, आप के साथ शुरू होता है:

  -> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36) 
       Filter: (name IS NULL) 

भागो EXPLAIN ANALYZE SELECT * FROM options WHERE name IS NULL; और लौट आए पंक्तियों अनुमान से मेल खाता है या नहीं। 2 ऑफ का एक कारक आम तौर पर एक समस्या नहीं है, आप परिमाण अंतर के क्रम को खोजने की कोशिश कर रहे हैं।

फिर EXPLAIN ANALYZE SELECT * FROM vehicles WHERE date_sold IS NULL; पंक्तियों की अपेक्षित राशि लौटाता है।

फिर हैश को एक स्तर ऊपर जाना शामिल हो:

 -> Hash Join (cost=5301.58..29722.32 rows=229 width=40) 
      Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text)) 

कर देखें कि कहीं EXPLAIN ANALYZE SELECT * FROM vehicles AS iv INNER JOIN options io ON (io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text) WHERE iv.date_sold IS NULL AND io.name IS NULL; परिणाम 229 पंक्तियों में।

एक और स्तर ऊपर INNER JOIN options co ON (co.fkey_style = iv.chrome_styleid) AND (co.code = io.code) जोड़ता है और केवल एक पंक्ति लौटने की उम्मीद है। शायद यह मुद्दा है क्योंकि यदि पंक्तियों की वास्तविक संख्या 1 से 100 तक जाती है, तो युक्त नेस्टेड लूप के भीतरी पाश को पार करने का कुल लागत अनुमान 100 के कारक से बंद होता है।

अंतर्निहित गलती योजनाकार संभवतः यह उम्मीद कर रहा है कि co में शामिल होने के लिए दोनों भविष्यवाणी करते हैं कि वे एक-दूसरे से स्वतंत्र हैं और उनकी चुनिंदाताओं को गुणा करते हैं। जबकि वास्तविकता में वे भारी सहसंबंधित हो सकते हैं और चयनकता MIN (s1, s2) के करीब है s1 * s2 नहीं।

+0

यह एक अच्छा जवाब है, लेकिन जब आप 'सह में शामिल होने' कहते हैं तो आप किसके बारे में बात कर रहे हैं? मुझे लगता है कि समस्या का 'रोडियाडोड्स' स्पष्टीकरण क्योंकि यह सटीक लगता है? क्या आप एक ही चीज़ या कुछ अलग बता रहे हैं? –

+0

वही बात। 'सह' में शामिल होना 'नेस्टेड लूप में शामिल होने के विकल्प co' के आंतरिक नोड पर विकल्प_पीकी का उपयोग करके इंडेक्स स्कैन है। इसमें दो स्थितियां हैं जिनकी योजनाकार शायद अवास्तविक रूप से सोचता है, परिणामस्वरूप आउटपुट की एक पंक्ति होगी।यदि आप उस क्वेरी को चलाने का प्रयास करते हैं और देखते हैं कि वास्तव में कितनी पंक्तियां आती हैं तो आप यह सत्यापित कर सकते हैं कि यह मामला है या नहीं। सहसंबंधित भविष्यवाणियों के लिए खराब अनुमान एक ज्ञात मुद्दा हैं। प्रदर्शन सूची पर इसके बारे में कुछ चर्चाएं हैं: http://archives.postgresql.org/pgsql-performance/2009-06/msg00055.php –

2

क्या आपने टेबल का विश्लेषण किया था? और इन तालिकाओं के बारे में pg_stats को क्या कहना है? क्वेरीप्लान आंकड़ों पर आधारित है, इन्हें ठीक होना है। और आप किस संस्करण का उपयोग करते हैं? 8.4?

प्लानर लागत स्थिरांक के लिए postgresql.conf में आंकड़ों, राशि की राशि, पंक्तियों की राशि और सेटिंग्स का उपयोग करके लागत की गणना की जा सकती है।

work_mem भी शामिल है, यह बहुत कम हो सकता है और प्रदर्शन को मारने के लिए एक seqscan करना है, योजनाकार मजबूर हो सकता है ...

+0

मैंने शामिल सभी तालिकाओं पर 'वैक्यूम पूर्ण विश्लेषण' चलाया, और यह पोस्टग्रेस्क्ल 8.4 है। –

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