PostgreSQL में अनुक्रमित का कार्य


73

PostgreSQL में अनुक्रमित के काम के बारे में मेरे पास कुछ सवाल हैं। मेरे पास Friendsनिम्नलिखित सूचकांक के साथ एक तालिका है:

   Friends ( user_id1 ,user_id2) 

user_id1और टेबल की user_id2विदेशी चाबियां हैंuser

  1. क्या ये बराबर हैं? अगर नहीं तो क्यों?

    Index(user_id1,user_id2) and Index(user_id2,user_id1)
  2. यदि मैं प्राथमिक कुंजी (user_id1, user_id2) बनाता हूं, तो क्या यह स्वचालित रूप से इसके लिए अनुक्रमित बनाता है और

    यदि पहले प्रश्न में अनुक्रमणिका समतुल्य नहीं हैं, तो प्राथमिक प्रमुख कमांड के ऊपर कौन सा सूचकांक बनाया गया है?

जवाबों:


77

यहां एक बहुरंगी सूचकांक के दूसरे स्तंभ पर एक तालिका को क्वेरी करने के परिणाम हैं ।
प्रभाव किसी के लिए भी पुन: पेश करना आसान है। बस इसे घर पर आज़माएं।

मैंने 23322 पंक्तियों के साथ वास्तविक जीवन डेटाबेस के एक मध्यम आकार की तालिका का उपयोग करके डेबियन पर PostgreSQL 9.0.5 के साथ परीक्षण किया । यह तालिकाओं adr(पता) और att(विशेषता) के बीच n: m संबंध को लागू करता है , लेकिन यहां प्रासंगिक नहीं है। सरलीकृत स्कीमा:

CREATE TABLE adratt (
  adratt_id serial PRIMARY KEY
, adr_id    integer NOT NULL
, att_id    integer NOT NULL
, log_up    timestamp(0) NOT NULL DEFAULT (now())::timestamp(0)
, CONSTRAINT adratt_uni UNIQUE (adr_id, att_id)
);

UNIQUEबाधा प्रभावी रूप से एक अद्वितीय सूचकांक लागू करता है। मैंने यह सुनिश्चित करने के लिए एक सादे सूचकांक के साथ परीक्षण दोहराया और उम्मीद के मुताबिक समान परिणाम प्राप्त किए।

CREATE INDEX adratt_idx ON adratt(adr_id, att_id)

तालिका adratt_uniसूचकांक पर क्लस्टर की गई है और परीक्षण से पहले मैं भागा:

CLUSTER adratt;
ANALYZE adratt;

प्रश्नों के अनुक्रमिक स्कैन (adr_id, att_id)उतने ही तेज़ होते हैं जितने संभवतः हो सकते हैं। बहुरंगी सूचकांक अभी भी अकेले दूसरे सूचकांक कॉलम पर एक क्वेरी स्थिति के लिए उपयोग किया जाएगा।

मैंने कैश को पॉप्युलेट करने के लिए प्रश्नों को एक-दो बार चलाया और तुलनीय परिणाम प्राप्त करने के लिए दस में से सर्वश्रेष्ठ अंक चुने।

1. दोनों स्तंभों का उपयोग करके क्वेरी

SELECT *
FROM   adratt
WHERE  att_id = 90
AND    adr_id = 10;

 adratt_id | adr_id | att_id |       log_up
-----------+--------+--------+---------------------
       123 |     10 |     90 | 2008-07-29 09:35:54
(1 row)

का आउटपुट EXPLAIN ANALYZE:

Index Scan using adratt_uni on adratt  (cost=0.00..3.48 rows=1 width=20) (actual time=0.022..0.025 rows=1 loops=1)
  Index Cond: ((adr_id = 10) AND (att_id = 90))
Total runtime: 0.067 ms

2. पहले कॉलम का उपयोग करते हुए क्वेरी करें

SELECT * FROM adratt WHERE adr_id = 10

 adratt_id | adr_id | att_id |       log_up
-----------+--------+--------+---------------------
       126 |     10 |     10 | 2008-07-29 09:35:54
       125 |     10 |     13 | 2008-07-29 09:35:54
      4711 |     10 |     21 | 2008-07-29 09:35:54
     29322 |     10 |     22 | 2011-06-06 15:50:38
     29321 |     10 |     30 | 2011-06-06 15:47:17
       124 |     10 |     62 | 2008-07-29 09:35:54
     21913 |     10 |     78 | 2008-07-29 09:35:54
       123 |     10 |     90 | 2008-07-29 09:35:54
     28352 |     10 |    106 | 2010-11-22 12:37:50
(9 rows)

का आउटपुट EXPLAIN ANALYZE:

Index Scan using adratt_uni on adratt  (cost=0.00..8.23 rows=9 width=20) (actual time=0.007..0.023 rows=9 loops=1)
  Index Cond: (adr_id = 10)
Total runtime: 0.058 ms

3. दूसरे कॉलम का उपयोग करके क्वेरी

SELECT * FROM adratt WHERE att_id = 90

 adratt_id | adr_id | att_id |       log_up
-----------+--------+--------+---------------------
       123 |     10 |     90 | 2008-07-29 09:35:54
       180 |     39 |     90 | 2008-08-29 15:46:07
...
(83 rows)

का आउटपुट EXPLAIN ANALYZE:

Index Scan using adratt_uni on adratt  (cost=0.00..818.51 rows=83 width=20) (actual time=0.014..0.694 rows=83 loops=1)
  Index Cond: (att_id = 90)
Total runtime: 0.849 ms

4. इंडेक्सस्कैन और बिटमैप्सन को अक्षम करें

SET enable_indexscan = off;
SELECT * FROM adratt WHERE att_id = 90

उत्पादन की मात्रा का विस्तार:

Bitmap Heap Scan on adratt  (cost=779.94..854.74 rows=83 width=20) (actual time=0.558..0.743 rows=83 loops=1)
  Recheck Cond: (att_id = 90)
  ->  Bitmap Index Scan on adratt_uni  (cost=0.00..779.86 rows=83 width=0) (actual time=0.544..0.544 rows=83 loops=1)
        Index Cond: (att_id = 90)
Total runtime: 0.894 ms
SET enable_bitmapscan = off
SELECT * FROM adratt WHERE att_id = 90

का आउटपुट EXPLAIN ANALYZE:

Seq Scan on adratt  (cost=0.00..1323.10 rows=83 width=20) (actual time=0.009..2.429 rows=83 loops=1)
  Filter: (att_id = 90)
Total runtime: 2.680 ms

निष्कर्ष

जैसा कि अपेक्षित था, मल्टी-कॉलम इंडेक्स का उपयोग अकेले दूसरे कॉलम पर क्वेरी के लिए किया जाता है।
जैसा कि अपेक्षित था, यह कम प्रभावी है, लेकिन क्वेरी अभी भी सूचकांक के बिना 3x तेज है।
इंडेक्स स्कैन को अक्षम करने के बाद, क्वेरी प्लानर एक बिटमैप हीप स्कैन चुनता है, जो लगभग तेजी से कार्य करता है। इसे अक्षम करने के बाद ही, यह एक अनुक्रमिक स्कैन में वापस आ जाता है।


क्लस्टरिंग होगा एक फर्क अगर सूचकांक में मैचों की संख्या काफी अधिक है (देखें यहाँ सबूत के लिए - डबल रन ध्यान दें डेटा कैश्ड पाने के लिए)
जैक डगलस

1
@JackDouglas: मैंने इसे कुछ और सोचा है। क्लस्टरिंग आम तौर पर मदद कर सकता है , क्योंकि यह प्रभावी रूप से एक vacuum fullऔर एक भी है reindex। की तुलना में है कि यह पहली या दोनों प्रमुख स्तंभों पर सूचकांक स्कैन में मदद मिलेगी अन्य एक बहुत है, लेकिन चोट दूसरे स्तंभ पर किए जाते हैं। एक ताज़ा क्लस्टर तालिका में, दूसरे स्तंभ में समान मान वाली पंक्तियाँ फैली हुई हैं, ताकि अधिकतम ब्लॉक को पढ़ना पड़े।
एरविन ब्रान्डसेट्टर

28

पुनः 1) हां और नहीं।

क्वेरी के लिए जो दोनों स्तंभों का उपयोग करता है उदाहरण के लिए where (user_id1, user_id2) = (1,2)यह कोई फर्क नहीं पड़ता कि कौन सा सूचकांक बनाया गया है।

ऐसे क्वेरी के लिए जिसमें कॉलम में से केवल एक पर स्थिति होती है, जैसे where user_id1 = 1कि यह मायने रखता है क्योंकि आमतौर पर केवल "अग्रणी" कॉलम का उपयोग ऑप्टिमाइज़र द्वारा तुलना के लिए किया जा सकता है। इसलिए where user_id1 = 1इंडेक्स (user_id1, user_id2) का उपयोग करने में सक्षम होगा, लेकिन यह सभी मामलों के लिए एक इंडेक्स (user_id2, user_id1) नहीं कर पाएगा।

इस के साथ खेलने के बाद (इरविन के बाद कृपया हमें एक सेटअप दिखाया जहां यह काम करता है), ऐसा लगता है कि यह दूसरे कॉलम के डेटा वितरण पर अत्यधिक निर्भर करता है, हालांकि मुझे अभी तक यह पता नहीं चला है कि कौन सी स्थिति ऑप्टिमाइज़र को ट्रेलिंग कॉलम का उपयोग करने में सक्षम बनाती है WHERE की स्थिति के लिए।

Oracle 11 जो (कभी-कभी) उन स्तंभों का उपयोग कर सकता है जो सूचकांक परिभाषा की शुरुआत में नहीं हैं।

पुनः 2) हाँ यह एक सूचकांक बनाएगा

मैनुअल से उद्धरण

प्राथमिक कुंजी जोड़ने से स्वचालित रूप से प्राथमिक कुंजी में उपयोग किए जाने वाले स्तंभों के समूह या समूह पर एक अद्वितीय btree सूचकांक बन जाएगा।

2 ए फिर से) Primary Key (user_id1,user_id2)(user_id1, user_id2) (जो आप खुद ही पता कर सकते हैं पर एक सूचकांक पैदा करेगा बहुत बस इस तरह के एक प्राथमिक कुंजी बनाने के द्वारा आसानी से)

मैं अत्यधिक अनुशंसा करता हूं कि आप मैनुअल में अनुक्रमित के बारे में अध्याय पढ़ें , यह मूल रूप से उपरोक्त सभी सवालों के जवाब देता है।

इसके अतिरिक्त, क्या सूचकांक बनाने के लिए? द्वारा, सूचकांक कॉलम और अन्य सूचकांक से संबंधित विषयों पर आदेश की व्याख्या करते हुए एक अच्छा काम करता है।


11

विज्ञापन 1)
PostgreSQL में सीमाएँ हैं जैसे @a_horse_with_no_name वर्णन करता है । जब तक संस्करण 8.0 मल्टीकोल्यूमेंट इंडेक्स केवल प्रमुख कॉलम (एस) पर प्रश्नों के लिए इस्तेमाल किया जा सकता है। यह 8.1 संस्करण में सुधार किया गया है। Postgres 10 के लिए वर्तमान पुस्तिका (अद्यतन) बताते हैं:

एक बहुस्तरीय बी-ट्री इंडेक्स का उपयोग क्वेरी स्थितियों के साथ किया जा सकता है जिसमें इंडेक्स के कॉलम के किसी भी सबसेट को शामिल किया जाता है, लेकिन प्रमुख (सबसे बाएं) कॉलम पर अवरोध होने पर इंडेक्स सबसे अधिक कुशल होता है। सटीक नियम यह है कि प्रमुख स्तंभों पर समानता बाधा बनती है, साथ ही पहले स्तंभ पर कोई असमानता बाधाएं जो एक समानता बाधा नहीं है, का उपयोग स्कैन किए गए सूचकांक के हिस्से को सीमित करने के लिए किया जाएगा। इन स्तंभों के दाईं ओर स्तंभों में अवरोधों की जाँच की जाती है, इसलिए वे उचित तालिका में विज़िट को सहेजते हैं, लेकिन वे सूचकांक के उस हिस्से को कम नहीं करते हैं जिसे स्कैन किया जाना है। उदाहरण के लिए, एक इंडेक्स ऑन (a, b, c)और एक क्वेरी कंडीशन को देखते हुए WHERE a = 5 AND b >= 42 AND c < 77, इंडेक्स को पहली प्रविष्टि से a= 5 और के साथ स्कैन करना होगाb= a5. के साथ अंतिम प्रविष्टि के माध्यम से 42 तक = c> 77 के साथ सूचकांक प्रविष्टियों को छोड़ दिया जाएगा, लेकिन उन्हें अभी भी स्कैन करना होगा। इस सूचकांक को उन प्रश्नों के लिए इस्तेमाल किया जा सकता है, जिन पर b/ और cकोई बाधा नहीं है a- लेकिन पूरे सूचकांक को स्कैन करना होगा, इसलिए अधिकांश मामलों में सूचकांक सूचकांक का उपयोग करने पर अनुक्रमिक तालिका स्कैन को प्राथमिकता देगा।

जोर मेरा। मैं अनुभव से इसकी पुष्टि कर सकता हूं।
यह भी देखें कि परीक्षण के मामले ने मेरे बाद के उत्तर को यहां जोड़ा ।


11

यह जैक के जवाब के जवाब में है , एक टिप्पणी नहीं करेगा।

9.2 संस्करण से पहले PostgreSQL में कोई कवरिंग इंडेक्स नहीं थे । MVCC मॉडल के कारण, दृश्यता की जांच करने के लिए परिणाम सेट में प्रत्येक टपल का दौरा किया जाना चाहिए। आप ओरेकल के बारे में सोच रहे होंगे।

PostgreSQL डेवलपर्स "इंडेक्स-ओनली स्कैन" के बारे में बात करते हैं । वास्तव में, फीचर को पोस्टग्रेस 9.2 के साथ जारी किया गया है। कमिट मैसेज पढ़ें
डीपेज़ ने एक बहुत ही जानकारीपूर्ण ब्लॉग पोस्ट लिखा ।

ट्रू कवरिंग इंडेक्स (अपडेट) को INCLUDEपोस्टग्रेज 11 के साथ क्लॉज के साथ पेश किया गया है । संबंधित:

यह थोड़ा बंद है:

यह इस तथ्य पर निर्भर करता है कि सूचकांक में 'पूर्ण स्कैन' सूचकांक में दिखाई देने वाले अतिरिक्त स्तंभों के कारण अनुक्रमित तालिका के 'पूर्ण स्कैन' की तुलना में अक्सर तेज होता है।

जैसा कि मेरे अन्य उत्तर पर टिप्पणियों में बताया गया है मैंने दो पूर्णांकों की तालिका के साथ परीक्षण भी चलाया है और कुछ नहीं। सूचकांक तालिका के समान कॉलम रखता है। बीटीआरई सूचकांक का आकार तालिका के लगभग 2/3 है। फैक्टर का स्पीडअप समझाने के लिए पर्याप्त नहीं है। मैंने आपके सेटअप के आधार पर, दो कॉलमों के लिए सरलीकृत किया और 100000 पंक्तियों के साथ अधिक परीक्षण किया। मेरे PostgreSQL 9.0 स्थापना के परिणाम संगत थे।

यदि तालिका में अतिरिक्त कॉलम हैं, तो इंडेक्स के साथ स्पीडअप अधिक महत्वपूर्ण हो जाता है, लेकिन यह निश्चित रूप से यहां एकमात्र कारक नहीं है

प्रमुख बिंदुओं का योग करने के लिए:

  • मल्टी-कॉलम इंडेक्स का उपयोग गैर-अग्रणी कॉलम पर प्रश्नों के साथ किया जा सकता है, लेकिन स्पीडअप चयनात्मक मानदंड (परिणाम में पंक्तियों का छोटा प्रतिशत) के लिए केवल कारक 3 के आसपास है। परिणाम सेट में तालिका के बड़े हिस्से के लिए बड़े ट्यूपल्स के लिए कम।

  • यदि प्रदर्शन महत्वपूर्ण है, तो उन स्तंभों पर एक अतिरिक्त अनुक्रमणिका बनाएँ।

  • यदि सभी शामिल किए गए कॉलम एक इंडेक्स (इंडेक्स को कवर करते हुए) और सभी शामिल पंक्तियों (प्रति ब्लॉक) में शामिल हैं, तो सभी लेनदेन दिखाई देते हैं, आप 9.2 या बाद के संस्करण में "इंडेक्स-ओनली स्कैन" प्राप्त कर सकते हैं ।


7
  1. क्या ये बराबर हैं? अगर नहीं तो क्यों?

    सूचकांक (user_id1, user_id2) और सूचकांक (user_id2, user_id1)

ये समतुल्य नहीं हैं और आम तौर पर बोलने वाले सूचकांक (बार, बाज) फॉर्म के प्रश्नों के लिए कुशल नहीं होंगे select * from foo where baz=?

इरविन ने प्रदर्शित किया है कि इस तरह के सूचकांक वास्तव में एक क्वेरी को गति दे सकते हैं लेकिन यह प्रभाव सीमित है और उसी क्रम का नहीं है जैसा कि आप आमतौर पर एक सूचकांक में एक लुकअप में सुधार की उम्मीद करते हैं - यह इस तथ्य पर निर्भर करता है कि सूचकांक का 'पूर्ण स्कैन' अक्सर होता है अनुक्रमणिका में दिखाई नहीं देने वाले तालिका में अतिरिक्त स्तंभों के कारण अनुक्रमित तालिका के 'पूर्ण स्कैन' से तेज।

सारांश: गैर-प्रमुख स्तंभों पर भी अनुक्रमणिका प्रश्नों की मदद कर सकती है, लेकिन दो माध्यमिक और अपेक्षाकृत मामूली तरीकों में से एक में और नाटकीय तरीके से नहीं, आप सामान्य रूप से सूचकांक की अपेक्षा करते हैं क्योंकि यह btree संरचना के कारण मदद करने के लिए है

nb दो तरीकों से अनुक्रमणिका की मदद कर सकता है यदि सूचकांक की एक पूर्ण स्कैन तालिका के पूर्ण स्कैन की तुलना में काफी सस्ती है और या तो: 1. तालिका लुकअप सस्ते हैं (क्योंकि उनमें से कुछ हैं या वे क्लस्टर किए गए हैं), या 2. सूचकांक को कवर किया जा रहा है, इसलिए सभी उफ़ पर कोई तालिका लुकअप नहीं है , यहां इरविन की टिप्पणियों को देखें

testbed:

create table foo(bar integer not null, baz integer not null, qux text not null);

insert into foo(bar, baz, qux)
select random()*100, random()*100, 'some random text '||g from generate_series(1,10000) g;

क्वेरी 1 (कोई सूचकांक नहीं, 74 बफ़र्स को मारना ):

explain (buffers, analyze, verbose) select max(qux) from foo where baz=0;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=181.41..181.42 rows=1 width=32) (actual time=3.301..3.302 rows=1 loops=1)
   Output: max(qux)
   Buffers: shared hit=74
   ->  Seq Scan on stack.foo  (cost=0.00..181.30 rows=43 width=32) (actual time=0.043..3.228 rows=52 loops=1)
         Output: bar, baz, qux
         Filter: (foo.baz = 0)
         Buffers: shared hit=74
 Total runtime: 3.335 ms

क्वेरी 2 (इंडेक्स के साथ - ऑप्टिमाइज़र इंडेक्स को अनदेखा करता है - फिर से 74 बफ़र्स को मारता है ):

create index bar_baz on foo(bar, baz);

explain (buffers, analyze, verbose) select max(qux) from foo where baz=0;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=199.12..199.13 rows=1 width=32) (actual time=3.277..3.277 rows=1 loops=1)
   Output: max(qux)
   Buffers: shared hit=74
   ->  Seq Scan on stack.foo  (cost=0.00..199.00 rows=50 width=32) (actual time=0.043..3.210 rows=52 loops=1)
         Output: bar, baz, qux
         Filter: (foo.baz = 0)
         Buffers: shared hit=74
 Total runtime: 3.311 ms

क्वेरी 2 (सूचकांक के साथ - और हम इसे उपयोग करने के लिए अनुकूलक को ट्रिक करते हैं):

explain (buffers, analyze, verbose) select max(qux) from foo where bar>-1000 and baz=0;
                                                       QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=115.56..115.57 rows=1 width=32) (actual time=1.495..1.495 rows=1 loops=1)
   Output: max(qux)
   Buffers: shared hit=36 read=30
   ->  Bitmap Heap Scan on stack.foo  (cost=73.59..115.52 rows=17 width=32) (actual time=1.370..1.428 rows=52 loops=1)
         Output: bar, baz, qux
         Recheck Cond: ((foo.bar > (-1000)) AND (foo.baz = 0))
         Buffers: shared hit=36 read=30
         ->  Bitmap Index Scan on bar_baz  (cost=0.00..73.58 rows=17 width=0) (actual time=1.356..1.356 rows=52 loops=1)
               Index Cond: ((foo.bar > (-1000)) AND (foo.baz = 0))
               Buffers: shared read=30
 Total runtime: 1.535 ms

इसलिए सूचकांक के माध्यम से पहुंच इस मामले में 30 बफर्स ​​को मारते हुए दोगुनी तेजी से होती है - जो कि अनुक्रमण के संदर्भ में 'थोड़ा तेज' है!, और YMMV फ़िल्टर किए गए पंक्तियों और क्लस्टर विशेषताओं की संख्या के साथ तालिका और सूचकांक के सापेक्ष आकार पर निर्भर करता है; तालिका में डेटा का

इसके विपरीत, प्रमुख स्तंभ पर क्वेरीज़ सूचकांक की btree संरचना का उपयोग करती हैं - इस मामले में 2 बफ़र्स मारते हैं :

explain (buffers, analyze, verbose) select max(qux) from foo where bar=0;
                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=75.70..75.71 rows=1 width=32) (actual time=0.172..0.173 rows=1 loops=1)
   Output: max(qux)
   Buffers: shared hit=38
   ->  Bitmap Heap Scan on stack.foo  (cost=4.64..75.57 rows=50 width=32) (actual time=0.036..0.097 rows=59 loops=1)
         Output: bar, baz, qux
         Recheck Cond: (foo.bar = 0)
         Buffers: shared hit=38
         ->  Bitmap Index Scan on bar_baz  (cost=0.00..4.63 rows=50 width=0) (actual time=0.024..0.024 rows=59 loops=1)
               Index Cond: (foo.bar = 0)
               Buffers: shared hit=2
 Total runtime: 0.209 ms
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.