পোস্টগ্রিস সূচক স্ক্যানের পরিবর্তে ক্রমিক স্ক্যান করছে


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

এটি পোস্টগ্রিজ অপ্টিমাইজেশন সম্পর্কিত একটি জ্ঞাত সমস্যা। যদি স্বতন্ত্র মানগুলি কয়েকটি হয় - আপনার ক্ষেত্রে যেমন - এবং আপনি 8.4+ সংস্করণে হন তবে একটি পুনরাবৃত্ত ক্যোয়ারী ব্যবহার করে খুব দ্রুত কাজটি এখানে বর্ণিত হয়েছে: লুজ ইনডেক্সস্কান

আপনার ক্যোয়ারী পুনরায় লেখা যেতে পারে ( LATERAL9.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 ;

এরউইন ব্র্যান্ডস্টেটারের এই উত্তরের ক্যোয়ারের একটি বিশদ ব্যাখ্যা এবং বিভিন্ন প্রকারের (একটি সম্পর্কিত তবে ভিন্ন ইস্যুতে) রয়েছে: ব্যবহারকারী অনুসারে সর্বশেষ রেকর্ড পুনরুদ্ধার করতে গ্রোপ বি কোয়েরি অনুকূলিত করুন


6

সেরা ক্যোয়ারী ডেটা বিতরণের উপর নির্ভর করে

আপনার প্রতি তারিখে অনেকগুলি সারি রয়েছে, এটি প্রতিষ্ঠিত হয়েছে। যেহেতু ফলস্বরূপ আপনার কেসটি কেবলমাত্র 26 টি মানের হয়ে গেছে, নীচের সমস্ত সমাধান সূচিটি ব্যবহারের সাথে সাথেই নির্লজ্জভাবে দ্রুত হবে will
(আরও স্বতন্ত্র মানের জন্য কেসটি আরও আকর্ষণীয় হয়ে উঠবে))

মোটেই জড়িত pageid হওয়ার দরকার নেই (যেমন আপনি মন্তব্য করেছেন)।

সূচক

আপনার যা দরকার তা হ'ল একটি সহজ বিটি্রি সূচক "labelDate"
কলামে কয়েকটি NULL এর বেশি মান সহ, একটি আংশিক সূচক আরও কিছুকে সহায়তা করে (এবং আরও ছোট):

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

আপনি পরে স্পষ্ট করে বলেছেন:

0% NULL তবে আমদানি করার সময় কেবল জিনিসগুলি ঠিক করার পরে।

আংশিক সূচকটি এখনও নুওলু মান সহ সারিগুলির মধ্যবর্তী রাজ্যগুলিকে বিসর্জন দেওয়ার জন্য বোধগম্য হতে পারে । সূচকে অপ্রয়োজনীয় আপডেটগুলি এড়াতে হবে (ফলশ্রুতি সহ)।

প্রশ্ন

একটি অস্থায়ী পরিসীমা উপর ভিত্তি করে

যদি আপনার তারিখগুলি অবিচ্ছিন্ন ফাঁক দিয়ে অবিচ্ছিন্ন পরিসরে উপস্থিত হয় , তবে আমরা 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");

আলগা সূচক স্ক্যান

এটি তারিখগুলির যে কোনও বিতরণের সাথে খুব ভাল সম্পাদন করে (যতক্ষণ না আমাদের কাছে প্রতি তারিখের অনেকগুলি সারি থাকে)। মূলত @ টাইপকিউব ইতিমধ্যে সরবরাহ করেছেন । তবে কিছু সূক্ষ্ম পয়েন্ট রয়েছে এবং আমাদের প্রিয় সূচিটি যে কোনও জায়গায় ব্যবহার করা যেতে পারে তা আমাদের নিশ্চিত করা দরকার।

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 এর ফলস্বরূপ সারিগুলি বাদ দেওয়া দরকার। দেখা:

  • ব্যবহারকারী প্রতি সর্বশেষ রেকর্ড পুনরুদ্ধার করতে GROUP বাই ক্যোয়ারী অপ্টিমাইজ করুন

Asides

উদ্ধৃত, আইনী, লোয়ার কেস শনাক্তকারীরা আপনার জীবনকে আরও সহজ করে।
কিছুটা ডিস্কের স্থান বাঁচাতে অনুকূলভাবে আপনার টেবিলের সংজ্ঞায় কলামগুলি অর্ডার করুন:


-2

পোস্টগ্র্যাস্কল ডকুমেন্টেশন থেকে:

ক্লাস্টার নির্দিষ্ট সূচকে সূচক স্ক্যান ব্যবহার করে টেবিলটিকে পুনরায় বাছাই করতে পারে বা (যদি সূচকটি বি-ট্রি হয়) বাছাইয়ের পরে ক্রমিক স্ক্যান করে । এটি পরিকল্পনাকারী ব্যয় পরামিতি এবং উপলভ্য পরিসংখ্যান সম্পর্কিত তথ্যের ভিত্তিতে দ্রুত পদ্ধতিটি বেছে নেওয়ার চেষ্টা করবে।

লেবেলডেটে আপনার সূচক একটি বিট্রি ..

রেফারেন্স:

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


এমনকি `WHERE" লেবেল তারিখ "BETWEEN '2000-01-01' এবং '2020-01-01' এর মতো শর্তের পরেও একটি ক্রমিক স্ক্যান জড়িত।
চার্লি ক্লার্ক

এই মুহুর্তে ক্লাস্টারিং (যদিও তথ্যটি সেই ক্রমে মোটামুটি প্রবেশ করা হয়েছিল)। এটি এখনও কোয়েরি পরিকল্পনাকারীর সত্যিকার অর্থে এমনকি পুরো শুল্ক সহ কোনও সূচক ব্যবহার না করার ব্যাখ্যাটি ব্যাখ্যা করে না।
চার্লি ক্লার্ক

আপনিও সেশনের ক্রমিক স্ক্যানটি অক্ষম করার চেষ্টা করেছেন? set enable_seqscan=offযে কোনও ক্ষেত্রে ডকুমেন্টেশন পরিষ্কার। আপনি যদি ক্লাস্টার করেন তবে এটি একটি ক্রমিক স্ক্যান করবে।
ফ্যাবরিজিও মাজনি

হ্যাঁ, আমি সিক্যুয়ালি স্ক্যানটি অক্ষম করার চেষ্টা করেছি তবে এটি খুব বেশি পার্থক্য করেনি। এই অনুসন্ধানের গতিটি আসলে গুরুত্বপূর্ণ নয় কারণ আমি এটির জন্য অনুসন্ধান সারণী তৈরি করতে ব্যবহার করি যা পরে সত্যিকারের প্রশ্নের সাথে জয়েন্টস এর জন্য ব্যবহার করা যেতে পারে।
চার্লি ক্লার্ক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.