6

मैं PostgreSQL 9.2 पर अनुकूलन करने के लिए इस क्वेरी दिया हूँ:Postgresql क्वेरी अनुकूलन कोई आंतरिक/बाहरी अनुमति शामिल होने

SELECT C.name, COUNT(DISTINCT I.id) AS NumItems, COUNT(B.id) 
FROM Categories C INNER JOIN Items I ON(C.id = I.category) 
        INNER JOIN Bids B ON (I.id = B.item_id) 
GROUP BY C.name 

मेरे स्कूल असाइनमेंट के हिस्से के रूप। > 2ndary बी + पेड़, bids(item_id) - -> 2ndary बी + पेड़, और categories(id) -> प्राथमिक सूचकांक यहाँ,

अजीब हिस्सा है, PostgreSQL items(category):

मैं संबंधित मेज पर इन अनुक्रमित बनाया है मेरे आइटम, श्रेणियां, और बोलियों की तालिका पर अनुक्रमिक स्कैन कर रहा है, और जब मैं enable_seqscan=off सेट करता हूं, तो इंडेक्स खोज नीचे दिए गए परिणाम से अधिक भयानक साबित होती है।

जब मैं PostgreSQL में समझाता हूं तो यह परिणाम होता है: कृपया उन लोगों को हटाएं क्योंकि वे महत्वपूर्ण हैं!

GroupAggregate (cost=119575.55..125576.11 rows=20 width=23) (actual time=6912.523..9459.431 rows=20 loops=1) 
    Buffers: shared hit=30 read=12306, temp read=6600 written=6598 
    -> Sort (cost=119575.55..121075.64 rows=600036 width=23) (actual time=6817.015..8031.285 rows=600036 loops=1) 
     Sort Key: c.name 
     Sort Method: external merge Disk: 20160kB 
     Buffers: shared hit=30 read=12306, temp read=6274 written=6272 
     -> Hash Join (cost=9416.95..37376.03 rows=600036 width=23) (actual time=407.974..3322.253 rows=600036 loops=1) 
       Hash Cond: (b.item_id = i.id) 
       Buffers: shared hit=30 read=12306, temp read=994 written=992 
       -> Seq Scan on bids b (cost=0.00..11001.36 rows=600036 width=8) (actual time=0.009..870.898 rows=600036 loops=1) 
        Buffers: shared hit=2 read=4999 
       -> Hash (cost=8522.95..8522.95 rows=50000 width=19) (actual time=407.784..407.784 rows=50000 loops=1) 
        Buckets: 4096 Batches: 2 Memory Usage: 989kB 
        Buffers: shared hit=28 read=7307, temp written=111 
        -> Hash Join (cost=1.45..8522.95 rows=50000 width=19) (actual time=0.082..313.211 rows=50000 loops=1) 
          Hash Cond: (i.category = c.id) 
          Buffers: shared hit=28 read=7307 
          -> Seq Scan on items i (cost=0.00..7834.00 rows=50000 width=8) (actual time=0.004..144.554 rows=50000 loops=1) 
           Buffers: shared hit=27 read=7307 
          -> Hash (cost=1.20..1.20 rows=20 width=19) (actual time=0.062..0.062 rows=20 loops=1) 
           Buckets: 1024 Batches: 1 Memory Usage: 1kB 
           Buffers: shared hit=1 
           -> Seq Scan on categories c (cost=0.00..1.20 rows=20 width=19) (actual time=0.004..0.028 rows=20 loops=1) 
             Buffers: shared hit=1 
Total runtime: 9473.257 ms 

this plan on explain.depesz.com देखें।

मैं सिर्फ यह जानना चाहता हूं कि ऐसा क्यों होता है, यानी अनुक्रमिक स्कैन की तुलना में इंडेक्स क्वेरी को बेहद धीमी गति से क्यों बनाते हैं।

संपादित करें: मुझे लगता है कि मैंने पोस्टग्रेस्क्ल दस्तावेज़ों के माध्यम से कुछ सामानों को उजागर करने में कामयाब रहा है। पोस्टग्रेस्क्ल ने बोलियों और वस्तुओं जैसी कुछ तालिकाओं पर सीक स्कैन करने का निर्णय लिया क्योंकि यह अनुमान लगाया गया है कि इसे तालिका में प्रत्येक पंक्ति को पुनर्प्राप्त करना होगा (वास्तविक समय से पहले ब्रैकेट में पंक्तियों की संख्या और वास्तविक समय में पंक्तियों की संख्या की तुलना करें अंश)। सभी पंक्तियों को पुनः प्राप्त करने में अनुक्रमिक स्कैन बेहतर है। उस हिस्से में कुछ भी नहीं किया जा सकता है।

मैंने categories(name) के लिए अतिरिक्त अनुक्रमणिका बनाई है, और नीचे दिया गया परिणाम मेरे पास है। यह किसी भी तरह से सुधार हुआ है लेकिन अब हैश जॉइन को नेस्टेड लूप के साथ बदल दिया गया है। क्यों कोई सुराग?

GroupAggregate (cost=0.00..119552.02 rows=20 width=23) (actual time=617.330..7725.314 rows=20 loops=1) 
    Buffers: shared hit=178582 read=37473 written=14, temp read=2435 written=436 
    -> Nested Loop (cost=0.00..115051.55 rows=600036 width=23) (actual time=0.120..6186.496 rows=600036 loops=1) 
     Buffers: shared hit=178582 read=37473 written=14, temp read=2109 written=110 
     -> Nested Loop (cost=0.00..26891.55 rows=50000 width=19) (actual time=0.066..2827.955 rows=50000 loops=1) 
       Join Filter: (c.id = i.category) 
       Rows Removed by Join Filter: 950000 
       Buffers: shared hit=2 read=7334 written=1, temp read=2109 written=110 
       -> Index Scan using categories_name_idx on categories c (cost=0.00..12.55 rows=20 width=19) (actual time=0.039..0.146 rows=20 loops=1) 
        Buffers: shared hit=1 read=1 
       -> Materialize (cost=0.00..8280.00 rows=50000 width=8) (actual time=0.014..76.908 rows=50000 loops=20) 
        Buffers: shared hit=1 read=7333 written=1, temp read=2109 written=110 
        -> Seq Scan on items i (cost=0.00..7834.00 rows=50000 width=8) (actual time=0.007..170.464 rows=50000 loops=1) 
          Buffers: shared hit=1 read=7333 written=1 
     -> Index Scan using bid_itemid_idx on bids b (cost=0.00..1.60 rows=16 width=8) (actual time=0.016..0.036 rows=12 loops=50000) 
       Index Cond: (item_id = i.id) 
       Buffers: shared hit=178580 read=30139 written=13 
Total runtime: 7726.392 ms 

यदि यह बेहतर है तो here पर योजना देखें।

मैंने श्रेणी (आईडी) और items(category) पर इंडेक्स बनाकर इसे 114062.9 2 तक कम करने में कामयाब रहा है। पोस्टग्रेस्क्ल ने 114062.9 2 लागत प्राप्त करने के लिए दोनों इंडेक्स का उपयोग किया। हालांकि, अब पोस्टग्रेस्क्ल इंडेक्स का उपयोग न करके मेरे साथ खेल खेल रहा है! यह इतनी छोटी क्यों है?

+0

नाम से समूह करने के लिए, इसे बहुत अधिक नाम से सॉर्ट करना होगा, हालांकि यदि आप श्रेणियों पर एक अनुक्रमणिका डालते हैं। नाम यह बेहतर काम कर सकता है। मैं अलग-अलग (i.id) को गति देने और तेज़ करने के लिए आइटम्स पर एक इंडेक्स भी डालूंगा और b.item_id में शामिल हो जाऊंगा। –

+0

इसके अलावा, ध्यान रखें कि पोस्टग्रेस "योजना की व्याख्या" शायद आपको "वैक्यूम विश्लेषण" करने के बाद तक अच्छे नतीजे न दें। –

+0

उत्तर @PaulTomblin के लिए धन्यवाद। हाँ मैंने यह सुनिश्चित किया कि मैंने सटीक परिणाम प्राप्त करने के लिए योजना की व्याख्या करने से पहले वैक्यूम विश्लेषण का उपयोग किया था। आइटम।आईडी आइटम तालिका के लिए प्राथमिक कुंजी है इस प्रकार इसमें क्लस्टर सूचकांक होगा। – ImNoob

उत्तर

1

बिना पूछे EXPLAIN आउटपुट पोस्ट करने के लिए धन्यवाद, और EXPLAIN (BUFFERS, ANALYZE) के लिए धन्यवाद।

Sort Method: external merge Disk: 20160kB 

आप स्मृति में इस तरह कर सकता है:

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

SET work_mem = '50MB'; 

अपनी क्वेरी चलाने से पहले। यह सेटिंग postgresql.conf में प्रति-उपयोगकर्ता, प्रति-डेटाबेस या वैश्विक स्तर पर भी सेट की जा सकती है।

मुझे विश्वास नहीं है कि इंडेक्स जोड़ना बहुत लाभकारी होगा क्योंकि क्वेरी वर्तमान में संरचित है। इसे सभी तीन तालिकाओं से सभी पंक्तियों को पढ़ने और शामिल करने की आवश्यकता है, और हैश जॉइन ऐसा करने का सबसे तेज़ तरीका होने की संभावना है।

मुझे संदेह है कि उस क्वेरी को व्यक्त करने के अन्य तरीके हैं जो पूरी तरह से अलग और अधिक कुशल निष्पादन रणनीतियों का उपयोग करेंगे, लेकिन मुझे के बारे में एक मस्तिष्क-फीका हो रहा है, वे हो सकते हैं और समय बिताना नहीं चाहते चारों ओर खेलने के लिए डमी टेबल बनाने के लिए। अधिक work_mem को क्वेरी के रूप में महत्वपूर्ण रूप से सुधार करना चाहिए क्योंकि यह खड़ा है।

+0

धन्यवाद। निश्चित रूप से work_mem नाटकीय रूप से प्रदर्शन में सुधार करता है। दुर्भाग्य से मुझे अपने जवाब में इसका उपयोग करने की अनुमति नहीं है। मैं शामिल सभी विशेषताओं के लिए इंडेक्स जोड़ रहा हूं, और केवल एक ही उपयोगी है जो समूह को तेजी से समूह बनाने के लिए श्रेणियों (नाम) पर सूचकांक है। इसके अलावा, कोई अन्य सूचकांक इसे बेहतर बनाता है। अगर मैं इसे बेहतर अद्यतन करने का प्रबंधन करता हूं तो कोई अपडेट पोस्ट करेगा। – ImNoob

+0

इंडेक्स और विदेशी कुंजी बाधाओं के बीच एक अंतर है। ओपी को एफके बाधाओं की जरूरत है, इंडेक्स नहीं। इसके अलावा: जैसा कि @ क्रैग रिंगर ने कहा था: क्वेरी * सभी * पंक्तियों (एफके बाधा के लिए सभी 3 टेबलों से, या (शायद) सूचकांक और सही आंकड़ों का उपयोग करते समय कम होनी चाहिए। अंतिम प्रकार/समूह एक हत्यारा है, क्योंकि यह है अन्य महत्वपूर्ण आयामों के लिए ऑर्थोगोनल। – wildplasser

+0

@ImNoob यदि आप स्कीमा और कुछ डमी डेटा को sqlfiddle.com पर पोस्ट करते हैं तो क्वेरी के लिए पूरी तरह से अलग दृष्टिकोण के साथ प्रयोग करना आसान है। –

0

क्वेरी योजनाओं से हम देख सकते हैं कि:
1. परिणाम और श्रेणियों 20 रिकॉर्ड
2. श्रेणी के साथ आइटम आइटम के सभी राशि
के 5% कर रहे हैं "पंक्तियाँ फ़िल्टर जुड़ें द्वारा निकाला गया: 950000"
अनुक्रमिक स्कैन में "पंक्तियां = 50000"
3. मिलान की बोली पंक्तियों = 600036 है (क्या आप हमें बोलियों की कुल संख्या दे सकते हैं?)
4. प्रत्येक श्रेणी में बोली है?

इसलिए हम आइटम (श्रेणी) और बोलियों (item_id) पर अनुक्रमणिका का उपयोग करना चाहते हैं। हम स्मृति में फिट सॉर्ट करना चाहते हैं।

select 
    (select name from Categories where id = foo.category) as name, 
    count(foo.id), 
    sum(foo.bids_count) 
from 
    (select 
     id, 
     category, 
     (select count(item_id) from Bids where item_id = i.id) as bids_count 
    from Items i 
    where category in (select id from Categories) 
     and exists (select 1 from Bids where item_id = i.id) 
    ) as foo 
    group by foo.category 
    order by name 
बेशक

आप याद रखें कि यह सख्ती से 1 अंक में डेटा पर निर्भर करता है और 2

तो 4 सच है आप निकाल सकते हैं क्वेरी से हिस्सा मौजूद है की है।

कोई सलाह या विचार?

0

ध्यान दें कि यदि bids के आकार को व्यवस्थित और काफी है items से बड़ा तो यह वास्तव में सस्ता items दो बार (विशेष रूप से इसलिए यदि items रैम में फिट बैठता है) में शामिल होने के परिणाम से उन विशिष्ट आइटम आईडी बंद लेने के लिए की तुलना में (पार करने के लिए हो सकता है भले ही आप स्मृति में सॉर्ट करें)। इसके अलावा, पोस्टग्रेस पाइपलाइन डुप्लिकेट टेबल से डेटा खींचने के तरीके के आधार पर, प्रतिकूल लोड या मेमोरी स्थितियों के तहत भी सीमित जुर्माना हो सकता है (यह एक अच्छा सवाल होगा कि आप pgsql-general पर पूछ सकते हैं।) लें:

SELECT name, IC.cnt, BC.cnt FROM 
Categories C, 
(SELECT category, count(1) cnt from Items I GROUP BY category) IC, 
(SELECT category, count(1) cnt from Bids B INNER JOIN Items I ON (I.id = B.item_id) GROUP BY category) BC 
WHERE IC.category=C.id AND BC.category=id; 

कितना सस्ता है? कम से कम 4x पर्याप्त कैशिंग प्रदान की गई, यानी 610ms बनाम 2500ms (इन-मेमोरी सॉर्ट) 20 श्रेणियों, 50k आइटम और 600k बोलियों के साथ, और फाइल सिस्टम सिस्टम कैश फ्लश के बाद 2x से भी तेज है।

ध्यान दें कि उपर्युक्त आपकी मूल क्वेरी के लिए प्रत्यक्ष विकल्प नहीं है; एक के लिए यह मानता है कि श्रेणी आईडी और नामों के बीच 1: 1 मैपिंग है (जो कि एक बहुत ही उचित धारणा साबित हो सकती है; यदि नहीं, तो SUM(BC.cnt) और SUM(IC.cnt) जैसे आप GROUP BY name), लेकिन अधिक महत्वपूर्ण प्रति श्रेणी आइटम गिनती में ऐसे आइटम शामिल हैं जिनमें आपकी बोलियां नहीं हैं, आपके मूल INNER JOIN के विपरीत। यदि केवल बोली-वस्तु आइटम की आवश्यकता है तो आप आईसी सबक्वायरी में WHERE EXISTS (SELECT 1 FROM Bids B where item_id=I.id) जोड़ सकते हैं; यह Bids को दूसरी बार भी पार करेगा (मेरे मामले में जो मौजूदा ~ 600ms योजना के लिए ~ 200ms जुर्माना लगाया गया है, अभी भी 2400ms से कम है।)