प्राथमिक कुंजी पर इंडेक्स को सरल जुड़ने में उपयोग नहीं किया जाता है


16

मेरी निम्नलिखित तालिका और सूचकांक परिभाषाएँ हैं:

CREATE TABLE munkalap (
    munkalap_id serial PRIMARY KEY,
    ...
);

CREATE TABLE munkalap_lepes (
    munkalap_lepes_id serial PRIMARY KEY,
    munkalap_id integer REFERENCES munkalap (munkalap_id),
    ...
);

CREATE INDEX idx_munkalap_lepes_munkalap_id ON munkalap_lepes (munkalap_id);

निम्नलिखित क्वेरी में munkalap_id पर कोई भी अनुक्रमणिका क्यों नहीं है?

EXPLAIN ANALYZE SELECT ml.* FROM munkalap m JOIN munkalap_lepes ml USING (munkalap_id);

QUERY PLAN
Hash Join  (cost=119.17..2050.88 rows=38046 width=214) (actual time=0.824..18.011 rows=38046 loops=1)
  Hash Cond: (ml.munkalap_id = m.munkalap_id)
  ->  Seq Scan on munkalap_lepes ml  (cost=0.00..1313.46 rows=38046 width=214) (actual time=0.005..4.574 rows=38046 loops=1)
  ->  Hash  (cost=78.52..78.52 rows=3252 width=4) (actual time=0.810..0.810 rows=3253 loops=1)
        Buckets: 1024  Batches: 1  Memory Usage: 115kB
        ->  Seq Scan on munkalap m  (cost=0.00..78.52 rows=3252 width=4) (actual time=0.003..0.398 rows=3253 loops=1)
Total runtime: 19.786 ms

यदि मैं कोई फ़िल्टर जोड़ता हूं तो भी यह समान है:

EXPLAIN ANALYZE SELECT ml.* FROM munkalap m JOIN munkalap_lepes ml USING (munkalap_id) WHERE NOT lezarva;

QUERY PLAN
Hash Join  (cost=79.60..1545.79 rows=1006 width=214) (actual time=0.616..10.824 rows=964 loops=1)
  Hash Cond: (ml.munkalap_id = m.munkalap_id)
  ->  Seq Scan on munkalap_lepes ml  (cost=0.00..1313.46 rows=38046 width=214) (actual time=0.007..5.061 rows=38046 loops=1)
  ->  Hash  (cost=78.52..78.52 rows=86 width=4) (actual time=0.587..0.587 rows=87 loops=1)
        Buckets: 1024  Batches: 1  Memory Usage: 4kB
        ->  Seq Scan on munkalap m  (cost=0.00..78.52 rows=86 width=4) (actual time=0.014..0.560 rows=87 loops=1)
              Filter: (NOT lezarva)
Total runtime: 10.911 ms

जवाबों:


22

कई लोगों ने मार्गदर्शन सुना है कि "अनुक्रमिक स्कैन खराब हैं" और उन्हें अपनी योजनाओं से खत्म करना चाहते हैं, लेकिन यह इतना आसान नहीं है। यदि क्वेरी तालिका में प्रत्येक पंक्ति को कवर करने जा रही है, तो अनुक्रमिक स्कैन उन पंक्तियों को प्राप्त करने का सबसे तेज़ तरीका है। यही कारण है कि आपकी मूल जुड़ाव क्वेरी में seq स्कैन का उपयोग किया गया है, क्योंकि दोनों तालिकाओं में सभी पंक्तियों की आवश्यकता थी।

क्वेरी की योजना बनाते समय, पोस्टग्रैज के योजनाकार विभिन्न संभावित योजनाओं के तहत विभिन्न परिचालनों (संगणना, अनुक्रमिक और यादृच्छिक IO) की लागतों का अनुमान लगाते हैं और सबसे कम लागत वाली योजना का अनुमान लगाते हैं। घूर्णन भंडारण (डिस्क) से IO करते समय, यादृच्छिक IO आमतौर पर अनुक्रमिक IO की तुलना में काफी धीमा होता है, random_page_cost और seq_page_cost के लिए डिफ़ॉल्ट pg विन्यास लागत में 4: 1 अंतर का अनुमान लगाता है।

ये विचार एक सम्मिलित या फ़िल्टर विधि पर विचार करते समय खेलने में आते हैं जो एक सूचकांक बनाम एक का उपयोग करता है जो क्रमिक रूप से एक तालिका को स्कैन करता है। सूचकांक का उपयोग करते समय, योजना सूचकांक के माध्यम से एक पंक्ति को जल्दी से ढूंढ सकती है, फिर पंक्ति डेटा को हल करने के लिए एक यादृच्छिक ब्लॉक रीड के लिए खाता होना चाहिए। अपनी दूसरी क्वेरी के मामले में, जिसमें एक फ़िल्टरिंग विधेय शामिल है WHERE NOT lezarva, आप देख सकते हैं कि इसने एंग्लज परिणाम के नियोजन अनुमानों को कैसे प्रभावित किया। योजनाकार जुड़ने के परिणामस्वरूप 1006 पंक्तियों का अनुमान लगाता है (जो कि 964 के वास्तविक परिणाम सेट से बहुत निकटता से मेल खाता है)। यह देखते हुए कि बड़ी तालिका munkalap_lepes में लगभग 38K पंक्तियाँ हैं, योजनाकार देखता है कि सम्मिलित होने के लिए तालिका में पंक्तियों के लगभग 1006/38046 या 1/38 का उपयोग करना होगा। यह भी जानता है कि औसत पंक्ति की चौड़ाई 214 बाइट्स है और एक ब्लॉक 8K है, इसलिए लगभग 38 पंक्तियाँ / ब्लॉक हैं।

इन आँकड़ों के साथ, योजनाकार इस संभावना पर विचार करता है कि जुड़ने वालों को तालिका के सभी या अधिकांश डेटा ब्लॉक को पढ़ना होगा। चूंकि इंडेक्स लुकअप या तो मुफ्त नहीं हैं, और फ़िल्टर स्थिति का मूल्यांकन करने वाले ब्लॉक को स्कैन करने की गणना आईओ के सापेक्ष बहुत सस्ती है, इसलिए योजनाकार ने क्रमिक रूप से टेबल को स्कैन करने और इंडेक्स ओवरहेड और रैंडम रीड से बचने के लिए चुना है क्योंकि यह seq स्कैन की गणना करता है। तेज हो जाएगा।

वास्तविक दुनिया में, डेटा अक्सर ओएस पेज कैश के माध्यम से मेमोरी में उपलब्ध होता है, और इसलिए प्रत्येक ब्लॉक रीड को IO की आवश्यकता नहीं होती है। यह अनुमान लगाना काफी कठिन हो सकता है कि किसी दिए गए क्वेरी के लिए कैश कितना प्रभावी होगा, लेकिन पीजी प्लानर कुछ सरल अनुमानों का उपयोग करता है। विन्यास मूल्य effective_cache_size वास्तविक आईओ लागत उठाने की संभावना के योजनाकारों अनुमान को सूचित। एक बड़ा मूल्य यादृच्छिक IO के लिए कम लागत का अनुमान लगाने का कारण होगा और इस प्रकार इसे अनुक्रमिक स्कैन पर एक सूचकांक संचालित विधि की ओर पूर्वाग्रह कर सकता है।


धन्यवाद, यह अब तक का सबसे अच्छा (और सबसे संक्षिप्त) विवरण मैंने पढ़ा है। कुछ प्रमुख बिंदुओं को स्पष्ट किया।
dezso

1
बहुत बढ़िया स्पष्टीकरण। पंक्तियों / डेटा पृष्ठ की गणना हालांकि थोड़ी दूर है। आपको पृष्ठ शीर्ष (24 बाइट्स) + 4 बाइट्स में प्रति-पंक्ति आइटम पॉइंटर + पंक्ति हेडर HeapTupleHeader(प्रति पंक्ति 23 बाइट्स) + NULL बिटमास्क + संरेखण MAXALIGN के अनुसार कारक है। अंत में, स्तंभों के डेटा प्रकार और उनके अनुक्रम के आधार पर डेटा संरेखण के कारण पैडिंग की एक अज्ञात राशि। सभी के सभी इस मामले में 8 kb पृष्ठ पर 33 से अधिक पंक्तियाँ नहीं हैं। (टोस्ट को ध्यान में नहीं रखना।)
इरविन ब्रान्डेसटेटर

1
@ErwinBrandstetter अधिक सटीक पंक्ति आकार गणना में भरने के लिए धन्यवाद। मैंने हमेशा यह माना था कि पंक्ति की चौड़ाई का अनुमान आउटपुट द्वारा समझा जाता है जिसमें हेडर और NULL-bitmask जैसे प्रति-पंक्ति विचार शामिल होंगे, लेकिन पृष्ठ स्तर उपरि नहीं।
debhur

1
@ दांभुर: आप EXPLAIN ANALYZE SELECT foo from barसत्यापित करने के लिए एक बुनियादी डमी तालिका के साथ एक त्वरित चला सकते हैं । इसके अलावा, वास्तविक ऑन-डिस्क स्थान डेटा संरेखण पर निर्भर करता है, जो केवल कुछ पंक्तियों को पुनः प्राप्त करने पर कारक के लिए कठिन होगा। EXPLAINस्तंभों के पुनर्प्राप्त सेट के लिए बुनियादी स्थान की आवश्यकता का प्रतिनिधित्व करने में पंक्ति चौड़ाई ।
इरविन ब्रान्डेसटेटर

5

आप दोनों तालिकाओं से सभी पंक्तियों को पुनः प्राप्त कर रहे हैं, इसलिए इंडेक्स स्कैन का उपयोग करके कोई वास्तविक लाभ नहीं है। एक इंडेक्स स्कैन केवल तभी समझ में आता है जब आप किसी तालिका से केवल कुछ पंक्तियों का चयन कर रहे हों (आमतौर पर 10% -15% से कम)


हां, आप सही हैं :) मैंने अधिक विशिष्ट मामले के साथ स्थिति को स्पष्ट करने की कोशिश की, अंतिम प्रश्न देखें।
dezso

@ डिडज़ो: एक ही बात। यदि आपके पास एक सूचकांक है (lezarva, munkalap_id)और यह काफी चयनात्मक है, तो इसका उपयोग किया जा सकता है। NOTकि कम संभावित बनाता है।
ypercube y

मैंने आपके सुझाव के आधार पर एक आंशिक सूचकांक जोड़ा और इसका उपयोग किया जाता है, इसलिए आधी समस्या हल हो गई है। लेकिन मैं विदेशी कुंजी पर सूचकांक के बेकार होने की उम्मीद नहीं करूंगा, जिसे मैं मूल
3252

1
@dezso पंक्तियाँ 214 बाइट्स चौड़ी हैं, इसलिए आपके पास 8K डेटा ब्लॉक में 40 पंक्तियों के नीचे थोड़ा सा होगा। सूचकांक की चयनात्मकता भी लगभग 1/40 (1006/38046) है। इसलिए, Pg के आंकड़े जो सभी ब्लॉकों को क्रमिक रूप से पढ़ रहे हैं सस्ता है, तो सूचकांक का उपयोग करते समय यादृच्छिक संख्या में लगभग समान ब्लॉकों की संभावित रीडिंग। ये अनुमानित ट्रेडऑफ़ प्रभावी_केच_साइज़ और रैंडम_पेज_कॉस्ट कॉन्फ़िगरेशन मूल्यों से प्रभावित हो सकते हैं।
debhur

@dbenhur: क्या आप अपनी टिप्पणी को उचित उत्तर दे सकते हैं?
dezso
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.