बहुत सारे डुप्लिकेट मानों के साथ किस इंडेक्स का उपयोग करना है?


14

आइए कुछ धारणाएँ बनाते हैं:

मेरे पास तालिका है जो इस तरह दिखती है:

 a | b
---+---
 a | -1
 a | 17
  ...
 a | 21
 c | 17
 c | -3
  ...
 c | 22

मेरे सेट के बारे में तथ्य:

  • पूरी तालिका का आकार ~ 10 10 पंक्तियाँ हैं।

  • मेरे पास aकॉलम में मूल्य के साथ ~ 100k पंक्तियां हैं a, अन्य मूल्यों के लिए समान (जैसे c)।

  • इसका मतलब है कि कॉलम 'ए' में ~ 100k अलग-अलग मान हैं।

  • मेरे अधिकांश प्रश्न किसी दिए गए मान के लिए सभी या अधिकांश मानों को पढ़ेंगे, जैसे select sum(b) from t where a = 'c'

  • तालिका इस तरह से लिखी गई है कि लगातार मान शारीरिक रूप से करीब हैं (या तो यह क्रम में लिखा गया है, या हम मानते हैं CLUSTERकि उस तालिका और स्तंभ पर उपयोग किया गया था a)।

  • तालिका शायद ही कभी अद्यतन की जाती है, हम केवल पढ़ने की गति के बारे में चिंतित हैं।

  • तालिका अपेक्षाकृत संकीर्ण है (प्रति बोले ~ 25 बाइट्स, + 23 बाइट्स ओवरहेड)।

अब सवाल यह है कि मुझे किस तरह के सूचकांक का उपयोग करना चाहिए? मेरी समझ है:

  • BTree मेरा मुद्दा यहाँ है कि BTree सूचकांक बहुत बड़ा होगा क्योंकि जहाँ तक मुझे पता है कि यह डुप्लिकेट मानों को संग्रहीत करेगा (यह है, क्योंकि यह नहीं मान सकता है कि तालिका भौतिक रूप से सॉर्ट की गई है)। यदि बीट्री विशाल है, तो मैं अंत में सूचकांक के अंक और तालिका के दोनों हिस्सों को पढ़ता हूं। (हम fillfactor = 100सूचकांक के आकार को थोड़ा कम करने के लिए उपयोग कर सकते हैं ।)

  • BRIN मेरी समझ यह है कि बेकार पन्नों को पढ़ने की कीमत पर मैं यहां एक छोटा सूचकांक रख सकता हूं। एक छोटे से उपयोग का pages_per_rangeमतलब है कि सूचकांक बड़ा है (जो कि BRIN के साथ एक समस्या है क्योंकि मुझे पूरे सूचकांक को पढ़ने की आवश्यकता है), एक बड़ा pages_per_rangeसाधन होने का मतलब है कि मैं बहुत सारे बेकार पृष्ठ पढ़ूंगा । क्या pages_per_rangeउन व्यापार-नापसंदों को ध्यान में रखने के लिए एक अच्छा फार्मूला है ?

  • GIN / GiST यकीन नहीं है कि वे प्रासंगिक हैं क्योंकि वे ज्यादातर पूर्ण-पाठ खोज के लिए उपयोग किए जाते हैं, लेकिन मैं यह भी सुनता हूं कि वे डुप्लिकेट कुंजियों से निपटने में अच्छे हैं। या तो एक GINया GiSTसूचकांक यहाँ मदद करेगा?

एक और सवाल है, क्या पोस्टग्रैज इस तथ्य का उपयोग करेंगे कि CLUSTERक्वेरी प्लानर में एक टेबल एड (कोई अपडेट नहीं मान रहा है) (उदाहरण के लिए बाइनरी प्रासंगिक शुरुआत / अंतिम पृष्ठों के लिए खोज)? कुछ हद तक संबंधित है, क्या मैं अपने सभी कॉलमों को बीट्री में स्टोर कर सकता हूं और टेबल को पूरी तरह से ड्रॉप कर सकता हूं (या कुछ समतुल्य प्राप्त कर सकता हूं, मेरा मानना ​​है कि वे एसक्यूएल सर्वर में क्लस्टर किए गए सूचकांक हैं)? क्या कुछ हाइब्रिड BTree / BRIN इंडेक्स है जो यहाँ मदद करेगा?

मैं अपने मूल्यों को संग्रहीत करने के लिए सरणियों का उपयोग करने से बचना चाहूंगा क्योंकि मेरी क्वेरी इस तरह से कम पठनीय हो जाएगी (मुझे लगता है कि यह ट्यूपल्स की संख्या को कम करके 23 बाइट्स प्रति ट्यूपल ओवरहेड की लागत को कम करेगा)।


"ज्यादातर पूर्ण-पाठ खोज के लिए उपयोग किया जाता है" पोस्ट जीआईएसटी द्वारा जीएसटी का काफी उपयोग किया जाता है।
jpmc26

जवाबों:


15

BTREE

यहाँ मेरा मुद्दा यह है कि BTree इंडेक्स भारी होगा क्योंकि यह डुप्लिकेट मानों को संग्रहीत करेगा (यह भी है, क्योंकि यह नहीं मान सकता है कि तालिका भौतिक रूप से सॉर्ट की गई है)। अगर BTree बहुत बड़ा है, तो मैं अंत में सूचकांक और तालिका के दोनों हिस्सों को पढ़ता हूं, जो सूचकांक को भी इंगित करता है ...

जरूरी नहीं - बीटीआर इंडेक्स होने के कारण जो 'कवर' है, वह सबसे तेजी से पढ़ा जाने वाला समय होगा, और यदि वह सब आप चाहते हैं (यानी यदि आप अतिरिक्त भंडारण का खर्च उठा सकते हैं), तो यह आपका सबसे अच्छा दांव है।

ब्रिन

मेरी समझ यह है कि बेकार पन्नों को पढ़ने की कीमत पर मैं यहां एक छोटा सूचकांक रख सकता हूं। एक छोटे से उपयोग का pages_per_rangeमतलब है कि सूचकांक बड़ा है (जो कि BRIN के साथ एक समस्या है क्योंकि मुझे पूरे सूचकांक को पढ़ने की आवश्यकता है), एक बड़ा pages_per_rangeसाधन होने का मतलब है कि मैं बहुत सारे बेकार पृष्ठ पढ़ूंगा ।

यदि आप एक कवरिंग btree इंडेक्स के स्टोरेज ओवरहेड को वहन नहीं कर सकते हैं, तो BRIN आपके लिए आदर्श है, क्योंकि आपके पास पहले से ही क्लस्टरिंग है (यह BRIN के उपयोगी होने के लिए महत्वपूर्ण है)। BRIN इंडेक्स छोटे होते हैं , इसलिए यदि आप उचित मूल्य चुनते हैं तो सभी पेज मेमोरी में होने की संभावना है pages_per_range

क्या उन ट्रेड ऑफ़्स को ध्यान में रखने वाले Pages_per_range का अच्छा मूल्य खोजने के लिए कोई जादू फार्मूला है?

कोई जादू सूत्र नहीं है, लेकिन औसत मूल्य के कब्जे वाले औसत आकार (पृष्ठों में) की pages_per_range तुलना में कुछ कम से शुरू होता है a। आप संभवतः कम करने की कोशिश कर रहे हैं: (BRIN स्कैन किए गए पृष्ठों की संख्या) + एक विशिष्ट क्वेरी के लिए (ढेर पृष्ठों की स्कैन की गई संख्या)। के लिए Heap Blocks: lossy=nनिष्पादन योजना के लिए देखें pages_per_range=1और अन्य मूल्यों के साथ तुलना करें pages_per_range- यानी देखें कि कितने अनावश्यक ढेर ब्लॉक स्कैन किए जा रहे हैं।

जिन / सार

सुनिश्चित नहीं हैं कि वे यहां प्रासंगिक हैं क्योंकि वे ज्यादातर पूर्ण पाठ खोज के लिए उपयोग किए जाते हैं, लेकिन मैं यह भी सुनता हूं कि वे डुप्लिकेट कुंजियों से निपटने में अच्छे हैं। या तो एक GIN/ GiSTसूचकांक यहाँ मदद करेगा?

जीआईएन विचार करने योग्य हो सकता है, लेकिन शायद जीएसटी नहीं - हालांकि अगर प्राकृतिक क्लस्टरिंग वास्तव में अच्छा है, तो ब्रिन शायद बेहतर दांव होगा।

यहाँ डमी डेटा के लिए अलग सूचकांक प्रकारों के बीच एक नमूना तुलना आपकी तरह है:

तालिका और अनुक्रमित:

create table foo(a,b,c) as
select *, lpad('',20)
from (select chr(g) a from generate_series(97,122) g) a
     cross join (select generate_series(1,100000) b) b
order by a;
create index foo_btree_covering on foo(a,b);
create index foo_btree on foo(a);
create index foo_gin on foo using gin(a);
create index foo_brin_2 on foo using brin(a) with (pages_per_range=2);
create index foo_brin_4 on foo using brin(a) with (pages_per_range=4);
vacuum analyze;

संबंध आकार:

select relname "name", pg_size_pretty(siz) "size", siz/8192 pages, (select count(*) from foo)*8192/siz "rows/page"
from( select relname, pg_relation_size(C.oid) siz
      from pg_class c join pg_namespace n on n.oid = c.relnamespace
      where nspname = current_schema ) z;
नाम | आकार | पेज | पंक्तियों / पेज
: ----------------- | : ------ | ----: | --------:
फू | 149 एमबी | 19118 | 135
foo_btree_covering | 56 एमबी | 7132 | 364
foo_btree | 56 एमबी | 7132 | 364
foo_gin | 2928 केबी | 366 | 7103
foo_brin_2 | 264 केबी | 33 | 78,787
foo_brin_4 | 136 केबी | 17 | 152,941

कवर btree:

explain analyze select sum(b) from foo where a='a';
| QUERY PLAN |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------------------- |
| सकल (लागत = 3282.57..3282.58 पंक्तियाँ = 1 चौड़ाई = 8) (वास्तविक समय = 45.942..45.942 पंक्तियाँ = 1 छोर = 1) |
| -> सूचकांक केवल foo_btree_covering पर foo (लागत = 0.43..3017.80 पंक्तियों = 105907 चौड़ाई = 4) का उपयोग करके स्कैन करें (वास्तविक समय = 0.038..27.286 पंक्तियों = 100000 छोरों = 1) |
| सूचकांक कंडोम: (a = 'a' :: text) |
| हीप कामोत्तेजक: 0 |
| योजना समय: 0.099 एमएस |
| निष्पादन समय: 45.968 एमएस |

सादा btree:

drop index foo_btree_covering;
explain analyze select sum(b) from foo where a='a';
| QUERY PLAN |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| सकल (लागत = 4064.57..4064.58 पंक्तियाँ = 1 चौड़ाई = 8) (वास्तविक समय = 54.242..54.242 पंक्तियाँ = 1 छोर = 1) |
| -> सूचकांक स्कैन foo_btree पर foo (लागत = 0.43..3799.80 पंक्तियों = 105907 चौड़ाई = 4) का उपयोग कर (वास्तविक समय = 0.037..33.084 पंक्तियों = 100000 छोरों = 1) |
| सूचकांक कंडोम: (a = 'a' :: text) |
| योजना समय: 0.135 एमएस |
| निष्पादन समय: 54.280 एमएस |

BRIN Pages_per_range = 4:

drop index foo_btree;
explain analyze select sum(b) from foo where a='a';
| QUERY PLAN |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| सकल (लागत = 21595.38..21595.39 पंक्तियाँ = 1 चौड़ाई = 8) (वास्तविक समय = 52.455..52.455 पंक्तियाँ = 1 छोर = 1) |
| -> बिटमैप ढेर स्कैन foo (लागत = 888.78..21330.61 पंक्तियों = 105907 चौड़ाई = 4) (वास्तविक समय = 2.738..31.967 पंक्तियों = 100000 छोरों = 1) |
| Recheck Cond: (a = 'a' :: text) |
| अनुक्रमणिका रीचेक द्वारा हटाए गए पंक्तियों: 96 |
| ढेर ब्लॉक: हानिपूर्ण = 736 |
| -> foo_brin_4 (लागत = 0.00..862.30 पंक्तियों = 105907 चौड़ाई = 0) पर बिटमैप इंडेक्स स्कैन (वास्तविक समय = 2.720..2.720 पंक्तियों = 7360 छोरों = 1) |
| सूचकांक कंडोम: (a = 'a' :: text) |
| योजना समय: 0.101 एमएस |
| निष्पादन समय: 52.501 एमएस |

BRIN Pages_per_range = 2:

drop index foo_brin_4;
explain analyze select sum(b) from foo where a='a';
| QUERY PLAN |
| : ------------------------------------------------- -------------------------------------------------- ----------------------------- |
| सकल (लागत = 21659.38..21659.39 पंक्तियाँ = 1 चौड़ाई = 8) (वास्तविक समय = 53.971..53.971 पंक्तियाँ = 1 छोर = 1) |
| -> बिटमैप ढेर स्कैन foo (लागत = 952.78..21394.61 पंक्तियों = 105907 चौड़ाई = 4) (वास्तविक समय = 5.286..33.492 पंक्तियों = 100000 छोरों = 1) |
| Recheck Cond: (a = 'a' :: text) |
| अनुक्रमणिका रीचेक द्वारा हटाए गए पंक्तियों: 96 |
| ढेर ब्लॉक: हानिपूर्ण = 736 |
| -> foo_brin_2 (लागत = 0.00..926.30 पंक्तियों = 105907 चौड़ाई = 0) पर बिटमैप इंडेक्स स्कैन (वास्तविक समय = 5.275..5.275 पंक्तियों = 7360 छोरों = 1) |
| सूचकांक कंडोम: (a = 'a' :: text) |
| योजना समय: 0.095 एमएस |
| निष्पादन समय: 54.016 एमएस |

जिन:

drop index foo_brin_2;
explain analyze select sum(b) from foo where a='a';
| QUERY PLAN |
| : ------------------------------------------------- -------------------------------------------------- ------------------------------ |
| समुच्चय (लागत = 21687.38..21687.39 पंक्तियों = 1 चौड़ाई = 8) (वास्तविक समय = 55.331..55.331 पंक्तियाँ = 1 छोर = 1) |
| -> बिटमैप ढेर स्कैन फू (लागत = 980.78..21422.61 पंक्तियों = 105907 चौड़ाई = 4) (वास्तविक समय = 12.377..33.956 पंक्तियों = 100000 छोरों = 1) |
| Recheck Cond: (a = 'a' :: text) |
| ढेर ब्लॉक: सटीक = 736 |
| -> बिटमैप इंडेक्स स्कैन foo_gin (लागत = 0.00..954.30 पंक्तियों = 105907 चौड़ाई = 0) (वास्तविक समय = 12.271..12.271 पंक्तियों = 100000 छोरों = 1) |
| सूचकांक कंडोम: (a = 'a' :: text) |
| योजना समय: 0.118 एमएस |
| निष्पादन समय: 55.366 एमएस |

यहाँ dbfiddle


तो एक आवरण सूचकांक डिस्क स्थान की कीमत पर तालिका को पूरी तरह से पढ़ना छोड़ देगा? एक अच्छे ट्रेडऑफ की तरह लगता है। मुझे लगता है कि हम BRIN इंडेक्स के लिए एक ही बात का मतलब 'पूरे इंडेक्स को पढ़ें' (मुझे गलत समझें तो सही करें), मेरा मतलब पूरे ब्राइन इंडेक्स को स्कैन करना है, जो मुझे लगता है कि dbfiddle.uk/… में हो रहा है , नहीं?
फू

@ के बारे में "(यह भी है, क्योंकि यह नहीं मान सकता है कि तालिका शारीरिक रूप से क्रमबद्ध है)।" तालिका का भौतिक क्रम (क्लस्टर या नहीं) अप्रासंगिक है। सूचकांक में सही क्रम में मूल्य हैं। लेकिन पोस्टग्रेज बी-ट्री इंडेक्स को सभी मूल्यों (और हां, कई बार) को स्टोर करना पड़ता है। इस प्रकार वे डिज़ाइन किए गए हैं। प्रत्येक विशिष्ट मूल्य को केवल एक बार संग्रहीत करना एक अच्छी सुविधा / सुधार होगा। आप इसे डेवलपर्स को पोस्टग्रेज करने के लिए सुझाव दे सकते हैं (और इसे लागू करने में मदद भी कर सकते हैं।) जैक को टिप्पणी करनी चाहिए, मुझे लगता है कि ओरेकल के बी-पेड़ों के कार्यान्वयन से ऐसा होता है।
ypercube y

1
@foo - आप पूरी तरह से सही हैं, BRIN इंडेक्स का एक स्कैन हमेशा पूरे इंडेक्स को स्कैन करता है ( pgcon.org/2016/schedule/attachments/… , 2nd last slide) - हालाँकि यह फिडल में समझाए गए प्लान पर नहीं दिखाया गया है , क्या यह?
जैक कहते हैं कि topanswers.xyz

2
@ ypercube y आप Oracle पर COMPRESS का उपयोग कर सकते हैं जो प्रत्येक ब्लॉक के अनुसार प्रत्येक अलग उपसर्ग को संग्रहीत करता है।
जैक कहते हैं कि topanswers.xyz

@JackDouglas मैंने Bitmap Index Scanअर्थ के रूप में पढ़ा 'पूरे ब्रिन इंडेक्स को पढ़ें' लेकिन शायद वह गलत रीड है। ओरेकल COMPRESSकुछ ऐसा दिखता है जो यहां उपयोगी होगा क्योंकि यह बी-पेड़ के आकार को कम करेगा, लेकिन मैं पीजी के साथ फंस गया हूं!
फू

6

इसके अलावा BTREE और ब्रिन जो सबसे समझदार विकल्प, कुछ अन्य, आकर्षक विकल्प जो लायक जांच कर रही हो सकता है लगता है - हो सकता है कि वे उपयोगी या अपने मामले में नहीं:

  • INCLUDEअनुक्रमित करता है । वे होंगे - उम्मीद है - पोस्टग्रेज के अगले प्रमुख संस्करण (10) में, सितंबर 2017 के आसपास कहीं। एक इंडेक्स पर एक इंडेक्स (a) INCLUDE (b)के समान संरचना है, (a)लेकिन पत्ती पृष्ठों में, b(लेकिन अनियंत्रित) के सभी मूल्य शामिल हैं । जिसका अर्थ है कि आप इसका उपयोग उदाहरण के लिए नहीं कर सकते हैं SELECT * FROM t WHERE a = 'a' AND b = 2 ;। इंडेक्स का उपयोग किया जा सकता है, लेकिन जब एक (a,b)इंडेक्स एक सीक के साथ मिलान पंक्तियों को ढूंढेगा, तो इंडेक्स को (संभवत: 100K आपके मामले में) मानों से गुजरना होगा जो मूल्यों से मेल खाते हैं a = 'a'और bजांचते हैं।
    दूसरी ओर, सूचकांक सूचकांक की तुलना में थोड़ा कम चौड़ा है (a,b)और आपको bगणना करने के लिए आपकी क्वेरी के लिए आदेश की आवश्यकता नहीं है SUM(b)। आप उदाहरण के लिए भी हो सकते हैं(a) INCLUDE (b,c,d) जिसका उपयोग आपके लिए उन सभी प्रश्नों के लिए किया जा सकता है जो सभी 3 कॉलमों पर एकत्रित होते हैं।

  • फ़िल्टर्ड (आंशिक) अनुक्रमित । एक सुझाव जो पहले * थोड़ा पागल लग सकता है :

    CREATE INDEX flt_a  ON t (b) WHERE (a = 'a') ;
    ---
    CREATE INDEX flt_xy ON t (b) WHERE (a = 'xy') ;

    प्रत्येक aमूल्य के लिए एक सूचकांक । आपके मामले में लगभग 100K इंडेक्स। हालांकि यह बहुत लगता है, पर विचार करें कि प्रत्येक सूचकांक बहुत छोटा होगा, दोनों आकार (पंक्तियों की संख्या) और चौड़ाई (क्योंकि यह सभी bमूल्यों को संग्रहीत करेगा )। हालांकि अन्य सभी पहलुओं में, यह (100K इंडेक्स एक साथ) एक बी-ट्री इंडेक्स के रूप में कार्य करेगा, (a,b)जबकि एक (b)इंडेक्स के स्थान का उपयोग करेगा ।
    नुकसान यह है कि आपको उन्हें स्वयं बनाना और बनाए रखना होगा, हर बार aतालिका में एक नया मान जोड़ा जाता है। चूँकि आपकी टेबल बल्कि कई (या किसी) आवेषण / अपडेट के बिना, स्थिर है, जो समस्या की तरह प्रतीत नहीं होती है।

  • सारांश सारणी। चूंकि तालिका स्थिर है, इसलिए आप हमेशा एक सारांश तालिका बना सकते हैं और सबसे आम समुच्चय के साथ पॉपुलेट कर सकते हैं, जिसकी आपको आवश्यकता होगी ( sum(b), sum(c), sum(d), avg(b), count(distinct b), आदि)। यह छोटी (केवल 100K पंक्तियाँ) होगी और इसे केवल एक बार पॉपुलेट करना होगा और केवल तभी अपडेट किया जाएगा जब मुख्य टेबल पर पंक्तियाँ डाली / अपडेट / हटाई जाएंगी।

* - इस कंपनी से कॉपी किए गए विचार जो उनके उत्पादन प्रणाली में 10 मिलियन इंडेक्स चलाता है: द हीप: रनिंग 10 मिलियन पोस्टग्रैक्सेल इंडेक्स इन प्रोडक्शन (और गिनती)


1 दिलचस्प है, लेकिन जैसा कि आप बताते हैं पृष्ठ 10 अभी तक बाहर नहीं है। 2 करता है ध्वनि पागल (या 'आम ज्ञान' के खिलाफ कम से कम), मैं के रूप में आप का कहना है के बाद से एक पढ़ा होगा कि मेरे लगभग कोई लिखता है कार्यप्रवाह के साथ काम कर सकता था। 3. नहीं मेरे लिए काम करते हैं, मैं प्रयोग किया जाता हैं SUMएक उदाहरण के रूप, लेकिन व्यवहार में मेरी प्रश्नों precomputed नहीं किया जा सकता (वे कर रहे हैं और अधिक की तरह select ... from t where a = '?' and ??wjere ??कुछ अन्य उपयोगकर्ता परिभाषित हालत होगी।
foo

1
अगर हम नहीं जानते कि हम क्या मदद नहीं कर सकते ???)
ypercube

आप फ़िल्टर किए गए अनुक्रमित का उल्लेख करते हैं। तालिका के विभाजन के बारे में क्या?
jpmc26

@ jpmc26 मजाकिया, मैं जवाब में जोड़ने की सोच रहा था कि फ़िल्टर्ड इंडेक्स का सुझाव एक तरह से विभाजन का एक रूप है। विभाजन भी यहाँ सहायक हो सकता है लेकिन मुझे यकीन नहीं है। इसके परिणामस्वरूप बहुत से छोटे सूचकांक / तालिकाओं का निर्माण होगा।
ypercube y

2
मुझे उम्मीद है कि आंशिक कवरिंग बीटीआरई इंडेक्स यहां प्रदर्शन का राजा होगा, क्योंकि डेटा लगभग कभी भी अपडेट नहीं होता है। भले ही इसका मतलब 100k इंडेक्स हो। कुल सूचकांक का आकार सबसे छोटा है (एक ब्रिन सूचकांक को छोड़कर, लेकिन वहाँ पोस्टग्रैज को ढेर पृष्ठों को पढ़ना और फ़िल्टर करना पड़ता है)। सूचकांक पीढ़ी को गतिशील एसक्यूएल के साथ स्वचालित किया जा सकता है। इस संबंधित उत्तर में उदाहरण DOकथन
इरविन ब्रान्डेसटेटर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.