अनुक्रमणिका स्कैन के बजाय Postgres अनुक्रमिक स्कैन कर रहा है


9

मेरे पास एक तालिका है जिसमें लगभग 10 मिलियन पंक्तियाँ हैं और एक दिनांक फ़ील्ड पर एक सूचकांक है। जब मैं अनुक्रमित फ़ील्ड के अनूठे मूल्यों को आज़माता हूं और निकालता हूं तो पोस्टग्रेज एक अनुक्रमिक स्कैन चलाता है भले ही परिणाम सेट में केवल 26 आइटम हों। आशावादी इस योजना को क्यों चुन रहा है? और मैं इससे क्या कर सकता हूं?

अन्य उत्तरों से मुझे संदेह है कि यह क्वेरी से संबंधित है जितना कि सूचकांक।

explain select "labelDate" from pages group by "labelDate";
                              QUERY PLAN
-----------------------------------------------------------------------
 HashAggregate  (cost=524616.78..524617.04 rows=26 width=4)
   Group Key: "labelDate"
   ->  Seq Scan on pages  (cost=0.00..499082.42 rows=10213742 width=4)
(3 rows)

तालिका संरचना:

http=# \d pages
                                       Table "public.pages"
     Column      |          Type          |        Modifiers
-----------------+------------------------+----------------------------------
 pageid          | integer                | not null default nextval('...
 createDate      | integer                | not null
 archive         | character varying(16)  | not null
 label           | character varying(32)  | not null
 wptid           | character varying(64)  | not null
 wptrun          | integer                | not null
 url             | text                   |
 urlShort        | character varying(255) |
 startedDateTime | integer                |
 renderStart     | integer                |
 onContentLoaded | integer                |
 onLoad          | integer                |
 PageSpeed       | integer                |
 rank            | integer                |
 reqTotal        | integer                | not null
 reqHTML         | integer                | not null
 reqJS           | integer                | not null
 reqCSS          | integer                | not null
 reqImg          | integer                | not null
 reqFlash        | integer                | not null
 reqJSON         | integer                | not null
 reqOther        | integer                | not null
 bytesTotal      | integer                | not null
 bytesHTML       | integer                | not null
 bytesJS         | integer                | not null
 bytesCSS        | integer                | not null
 bytesHTML       | integer                | not null
 bytesJS         | integer                | not null
 bytesCSS        | integer                | not null
 bytesImg        | integer                | not null
 bytesFlash      | integer                | not null
 bytesJSON       | integer                | not null
 bytesOther      | integer                | not null
 numDomains      | integer                | not null
 labelDate       | date                   |
 TTFB            | integer                |
 reqGIF          | smallint               | not null
 reqJPG          | smallint               | not null
 reqPNG          | smallint               | not null
 reqFont         | smallint               | not null
 bytesGIF        | integer                | not null
 bytesJPG        | integer                | not null
 bytesPNG        | integer                | not null
 bytesFont       | integer                | not null
 maxageMore      | smallint               | not null
 maxage365       | smallint               | not null
 maxage30        | smallint               | not null
 maxage1         | smallint               | not null
 maxage0         | smallint               | not null
 maxageNull      | smallint               | not null
 numDomElements  | integer                | not null
 numCompressed   | smallint               | not null
 numHTTPS        | smallint               | not null
 numGlibs        | smallint               | not null
 numErrors       | smallint               | not null
 numRedirects    | smallint               | not null
 maxDomainReqs   | smallint               | not null
 bytesHTMLDoc    | integer                | not null
 maxage365       | smallint               | not null
 maxage30        | smallint               | not null
 maxage1         | smallint               | not null
 maxage0         | smallint               | not null
 maxageNull      | smallint               | not null
 numDomElements  | integer                | not null
 numCompressed   | smallint               | not null
 numHTTPS        | smallint               | not null
 numGlibs        | smallint               | not null
 numErrors       | smallint               | not null
 numRedirects    | smallint               | not null
 maxDomainReqs   | smallint               | not null
 bytesHTMLDoc    | integer                | not null
 fullyLoaded     | integer                |
 cdn             | character varying(64)  |
 SpeedIndex      | integer                |
 visualComplete  | integer                |
 gzipTotal       | integer                | not null
 gzipSavings     | integer                | not null
 siteid          | numeric                |
Indexes:
    "pages_pkey" PRIMARY KEY, btree (pageid)
    "pages_date_url" UNIQUE CONSTRAINT, btree ("urlShort", "labelDate")
    "idx_pages_cdn" btree (cdn)
    "idx_pages_labeldate" btree ("labelDate") CLUSTER
    "idx_pages_urlshort" btree ("urlShort")
Triggers:
    pages_label_date BEFORE INSERT OR UPDATE ON pages
      FOR EACH ROW EXECUTE PROCEDURE fix_label_date()

जवाबों:


8

यह Postgres ऑप्टिमाइज़ेशन के बारे में एक ज्ञात समस्या है। यदि अलग-अलग मान कुछ हैं - जैसे आपके मामले में - और आप 8.4+ संस्करण में हैं, तो पुनरावर्ती क्वेरी का उपयोग करते हुए बहुत तेज़ वर्कअराउंड का वर्णन यहां किया गया है: लूज़ इंडेक्सस्कैन

आपकी क्वेरी फिर से लिखी जा सकती है ( LATERALआवश्यकता 9.3+ संस्करण):

WITH RECURSIVE pa AS 
( ( SELECT labelDate FROM pages ORDER BY labelDate LIMIT 1 ) 
  UNION ALL
    SELECT n.labelDate 
    FROM pa AS p
         , LATERAL 
              ( SELECT labelDate 
                FROM pages 
                WHERE labelDate > p.labelDate 
                ORDER BY labelDate 
                LIMIT 1
              ) AS n
) 
SELECT labelDate 
FROM pa ;

Erwin Brandstetter में इस उत्तर में (संबंधित लेकिन अलग-अलग मुद्दे पर) एक पूरी तरह से स्पष्टीकरण और क्वेरी के कई रूप हैं: प्रति उपयोगकर्ता नवीनतम रिकॉर्ड प्राप्त करने के लिए क्वेरी द्वारा ग्रुप ऑप्टिमाइज़ करें।


6

सर्वश्रेष्ठ क्वेरी बहुत अधिक डेटा वितरण पर निर्भर करती है

आपके पास प्रति दिनांक कई पंक्तियाँ हैं, जो स्थापित की गई हैं। चूंकि आपका मामला परिणाम में केवल 26 मानों तक जलता है, इसलिए सूचकांक का उपयोग करते ही निम्नलिखित सभी समाधान धमाकेदार तेजी से होंगे।
(अधिक विशिष्ट मूल्यों के लिए मामला अधिक दिलचस्प होगा।)

इसमें शामिल pageid होने की कोई आवश्यकता नहीं है (जैसे आपने टिप्पणी की)।

सूची

आप सभी की जरूरत पर एक साधारण btree सूचकांक है "labelDate"
कॉलम में कुछ से अधिक NULL मान के साथ, एक आंशिक सूचकांक कुछ और मदद करता है (और छोटा है):

CREATE INDEX pages_labeldate_nonull_idx ON big ("labelDate")
WHERE  "labelDate" IS NOT NULL;

आपने बाद में स्पष्ट किया:

0% पूर्ण लेकिन आयात करते समय चीजों को ठीक करने के बाद ही।

आंशिक सूचकांक अभी भी पूर्ण मानों के साथ पंक्तियों के मध्यस्थ राज्यों को बाहर करने के लिए समझ में आ सकता है । इंडेक्स के लिए अनावश्यक अपडेट से बचें (जिसके परिणामस्वरूप ब्लोट)।

सवाल

एक अनंतिम श्रेणी के आधार पर

यदि आपकी तारीखें निरंतर अंतराल में दिखाई देती हैं , तो बहुत अधिक अंतराल नहीं हैं , तो हम dateअपने लाभ के लिए डेटा प्रकार की प्रकृति का उपयोग कर सकते हैं । दो दिए गए मूल्यों के बीच केवल एक परिमित, गणनीय संख्या है। यदि अंतराल कम हैं, तो यह सबसे तेज़ होगा:

SELECT d."labelDate"
FROM  (
   SELECT generate_series(min("labelDate")::timestamp
                        , max("labelDate")::timestamp
                        , interval '1 day')::date AS "labelDate"
   FROM   pages
   ) d
WHERE  EXISTS (SELECT FROM pages WHERE "labelDate" = d."labelDate");

क्यों करने के लिए कलाकारों timestampमें generate_series()? देख:

मिन और मैक्स को इंडेक्स से सस्ते में लिया जा सकता है। यदि आप न्यूनतम और / या अधिकतम संभावित तारीख जानते हैं , तो यह थोड़ा सस्ता है, फिर भी। उदाहरण:

SELECT d."labelDate"
FROM  (SELECT date '2011-01-01' + g AS "labelDate"
       FROM   generate_series(0, now()::date - date '2011-01-01' - 1) g) d
WHERE  EXISTS (SELECT FROM pages WHERE "labelDate" = d."labelDate");

या, एक अपरिवर्तनीय अंतराल के लिए:

SELECT d."labelDate"
FROM  (SELECT date '2011-01-01' + g AS "labelDate"
       FROM generate_series(0, 363) g) d
WHERE  EXISTS (SELECT FROM pages WHERE "labelDate" = d."labelDate");

ढीला सूचकांक स्कैन

यह तारीखों के किसी भी वितरण के साथ बहुत अच्छा प्रदर्शन करता है (जब तक हमारे पास प्रति पंक्ति कई पंक्तियाँ हैं)। मूल रूप से क्या @ypercube पहले से ही प्रदान किया गया है । लेकिन कुछ ठीक बिंदु हैं और हमें यह सुनिश्चित करने की आवश्यकता है कि हमारे पसंदीदा सूचकांक का उपयोग हर जगह किया जा सकता है।

WITH RECURSIVE p AS (
   ( -- parentheses required for LIMIT
   SELECT "labelDate"
   FROM   pages
   WHERE  "labelDate" IS NOT NULL
   ORDER  BY "labelDate"
   LIMIT  1
   ) 
   UNION ALL
   SELECT (SELECT "labelDate" 
           FROM   pages 
           WHERE  "labelDate" > p."labelDate" 
           ORDER  BY "labelDate" 
           LIMIT  1)
   FROM   p
   WHERE  "labelDate" IS NOT NULL
   ) 
SELECT "labelDate" 
FROM   p
WHERE  "labelDate" IS NOT NULL;
  • पहला सीटीई pप्रभावी रूप से उसी के रूप में है

    SELECT min("labelDate") FROM pages

    लेकिन क्रिया रूप सुनिश्चित करता है कि हमारे आंशिक सूचकांक का उपयोग किया जाता है। इसके अलावा, यह फॉर्म आमतौर पर मेरे अनुभव (और मेरे परीक्षणों में) में थोड़ा तेज है।

  • केवल एक कॉलम के लिए, आरसीटीई के पुनरावर्ती कार्यकाल में सहसंबद्ध उपश्रेणियाँ थोड़ी तेज़ होनी चाहिए। इसके लिए "लेबलडैट" के लिए NULL के परिणामस्वरूप पंक्तियों को बाहर करना होगा। देख:

  • प्रति उपयोगकर्ता नवीनतम रिकॉर्ड प्राप्त करने के लिए क्वेरी द्वारा ग्रुप ऑप्टिमाइज़ करें

Asides

अयोग्य, कानूनी, निचले मामले के पहचानकर्ता आपके जीवन को आसान बनाते हैं।
कुछ डिस्क स्थान बचाने के लिए अपनी तालिका परिभाषा में स्तंभों को अनुकूल बनाएं:


-2

Postgresql प्रलेखन से:

ग्राहक तालिका को पुन: क्रमबद्ध कर सकता है या तो निर्दिष्ट सूचकांक पर एक इंडेक्स स्कैन का उपयोग कर, या (यदि इंडेक्स एक बी-ट्री है) क्रमिक स्कैन के बाद छँटाई । यह प्लानर लागत मापदंडों और उपलब्ध सांख्यिकीय जानकारी के आधार पर, उस विधि को चुनने का प्रयास करेगा जो तेजी से होगा।

लेबलडेट पर आपका सूचकांक एक btree है।

संदर्भ:

http://www.postgresql.org/docs/9.1/static/sql-cluster.html


यहां तक ​​कि `WHERE” लेबलडाइट “BETWEEN’ 2000-01-01 और -01 2020-01-01 ’जैसी स्थिति के साथ अभी भी एक अनुक्रमिक स्कैन शामिल है।
चार्ली क्लार्क

इस समय क्लस्टरिंग (हालांकि डेटा उस क्रम में मोटे तौर पर दर्ज किया गया था)। यह अभी भी वास्तव में क्वेरी प्लानर के फैसले को स्पष्ट नहीं करता है कि एक सूचकांक का उपयोग कहां भी किया जाए।
चार्ली क्लार्क

क्या आपने सत्र के लिए अनुक्रमिक स्कैन को अक्षम करने की भी कोशिश की है? set enable_seqscan=offकिसी भी मामले में प्रलेखन स्पष्ट है। यदि आप क्लस्टर करते हैं तो यह एक अनुक्रमिक स्कैन करेगा।
Fabrizio Mazzoni

हां, मैंने अनुक्रमिक स्कैन को अक्षम करने की कोशिश की लेकिन इससे बहुत फर्क नहीं पड़ा। इस क्वेरी की गति वास्तव में महत्वपूर्ण नहीं है क्योंकि मैं इसे लुकअप टेबल बनाने के लिए उपयोग करता हूं जिसे वास्तविक प्रश्नों में JOINS के लिए उपयोग किया जा सकता है।
चार्ली क्लार्क
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.