এসকিউএল ক্যোয়ারির জন্য সূচকগুলি যেখানে শর্ত এবং গ্রুপ দ্বারা with


15

আমি একটি WHEREশর্ত এবং GROUP BYকোনটি বর্তমানে খুব ধীরগতিতে চলছে তার সাথে এসকিউএল কোয়েরির জন্য কোন সূচকগুলি ব্যবহার করবেন তা নির্ধারণ করার চেষ্টা করছি ।

আমার প্রশ্ন:

SELECT group_id
FROM counter
WHERE ts between timestamp '2014-03-02 00:00:00.0' and timestamp '2014-03-05 12:00:00.0'
GROUP BY group_id

সারণীতে বর্তমানে 32.000.000 সারি রয়েছে। যখন আমি সময়সীমা বাড়িয়েছি তখন ক্যোয়ারির সম্পাদনের সময় অনেক বেড়ে যায়।

প্রশ্নের টেবিলটি দেখতে এমন দেখাচ্ছে:

CREATE TABLE counter (
    id bigserial PRIMARY KEY
  , ts timestamp NOT NULL
  , group_id bigint NOT NULL
);

আমার কাছে বর্তমানে নিম্নলিখিত সূচকগুলি রয়েছে তবে কার্য সম্পাদন এখনও ধীর:

CREATE INDEX ts_index
  ON counter
  USING btree
  (ts);

CREATE INDEX group_id_index
  ON counter
  USING btree
  (group_id);

CREATE INDEX comp_1_index
  ON counter
  USING btree
  (ts, group_id);

CREATE INDEX comp_2_index
  ON counter
  USING btree
  (group_id, ts);

ক্যোয়ারিতে এক্সপ্লেইন চালানো নিম্নলিখিত ফলাফল দেয়:

"QUERY PLAN"
"HashAggregate  (cost=467958.16..467958.17 rows=1 width=4)"
"  ->  Index Scan using ts_index on counter  (cost=0.56..467470.93 rows=194892 width=4)"
"        Index Cond: ((ts >= '2014-02-26 00:00:00'::timestamp without time zone) AND (ts <= '2014-02-27 23:59:00'::timestamp without time zone))"

উদাহরণস্বরূপ ডেটা সহ এসকিউএল ফিডল: http://sqlfiddle.com/#!15/7492b/1

প্রশ্নটি

আরও ভাল সূচকগুলি যুক্ত করে এই ক্যোয়ারির পারফরম্যান্স উন্নত করা যেতে পারে, বা আমার প্রসেসিং শক্তি বাড়ানো উচিত?

সম্পাদনা 1

PostgreSQL সংস্করণ 9.3.2 ব্যবহৃত হয়।

সম্পাদনা 2

আমি এরউইনের প্রস্তাবটি এর সাথে চেষ্টা করেছিলাম EXISTS:

SELECT group_id
FROM   groups g
WHERE  EXISTS (
   SELECT 1
   FROM   counter c
   WHERE  c.group_id = g.group_id
   AND    ts BETWEEN timestamp '2014-03-02 00:00:00'
                 AND timestamp '2014-03-05 12:00:00'
   );

তবে দুর্ভাগ্যক্রমে এগুলি পারফরম্যান্স বাড়িয়ে তোলে বলে মনে হয় না। অনুসন্ধান পরিকল্পনা:

"QUERY PLAN"
"Nested Loop Semi Join  (cost=1607.18..371680.60 rows=113 width=4)"
"  ->  Seq Scan on groups g  (cost=0.00..2.33 rows=133 width=4)"
"  ->  Bitmap Heap Scan on counter c  (cost=1607.18..158895.53 rows=60641 width=4)"
"        Recheck Cond: ((group_id = g.id) AND (ts >= '2014-01-01 00:00:00'::timestamp without time zone) AND (ts <= '2014-03-05 12:00:00'::timestamp without time zone))"
"        ->  Bitmap Index Scan on comp_2_index  (cost=0.00..1592.02 rows=60641 width=0)"
"              Index Cond: ((group_id = g.id) AND (ts >= '2014-01-01 00:00:00'::timestamp without time zone) AND (ts <= '2014-03-05 12:00:00'::timestamp without time zone))"

সম্পাদনা 3

ইপারক्यूब থেকে ল্যাটারাল ক্যোয়ারির জন্য ক্যোয়ারী পরিকল্পনা:

"QUERY PLAN"
"Nested Loop  (cost=8.98..1200.42 rows=133 width=20)"
"  ->  Seq Scan on groups g  (cost=0.00..2.33 rows=133 width=4)"
"  ->  Result  (cost=8.98..8.99 rows=1 width=0)"
"        One-Time Filter: ($1 IS NOT NULL)"
"        InitPlan 1 (returns $1)"
"          ->  Limit  (cost=0.56..4.49 rows=1 width=8)"
"                ->  Index Only Scan using comp_2_index on counter c  (cost=0.56..1098691.21 rows=279808 width=8)"
"                      Index Cond: ((group_id = $0) AND (ts IS NOT NULL) AND (ts >= '2010-03-02 00:00:00'::timestamp without time zone) AND (ts <= '2014-03-05 12:00:00'::timestamp without time zone))"
"        InitPlan 2 (returns $2)"
"          ->  Limit  (cost=0.56..4.49 rows=1 width=8)"
"                ->  Index Only Scan Backward using comp_2_index on counter c_1  (cost=0.56..1098691.21 rows=279808 width=8)"
"                      Index Cond: ((group_id = $0) AND (ts IS NOT NULL) AND (ts >= '2010-03-02 00:00:00'::timestamp without time zone) AND (ts <= '2014-03-05 12:00:00'::timestamp without time zone))"

group_idটেবিলে কতগুলি বিভিন্ন মান আছে?
ypercubeᵀᴹ

এখানে 133 টি আলাদা গ্রুপ_আইডি রয়েছে।

টাইমস্ট্যাম্পগুলি ২০১১ থেকে ২০১৪ অবধি রয়েছে seconds উভয় সেকেন্ড এবং মিলিসেকেন্ডগুলি ব্যবহৃত।

আপনি কি group_idকোনও গণনায় আগ্রহী এবং না?
এরউইন ব্র্যান্ডসেটেটার

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

উত্তর:


6

আরেকটি ধারণা, এতে groupsটেবিল এবং LATERALজোড় নামক একটি নির্মাণও ব্যবহৃত হয় (এসকিউএল-সার্ভার অনুরাগীদের জন্য, এটি প্রায় অনুরূপ OUTER APPLY)। উপকারে সমষ্টিগুলি গণনা করা যায় এমন সুবিধা রয়েছে:

SELECT group_id, min_ts, max_ts
FROM   groups g,                    -- notice the comma here, is required
  LATERAL 
       ( SELECT MIN(ts) AS min_ts,
                MAX(ts) AS max_ts
         FROM counter c
         WHERE c.group_id = g.group_id
           AND c.ts BETWEEN timestamp '2011-03-02 00:00:00'
                        AND timestamp '2013-03-05 12:00:00'
       ) x 
WHERE min_ts IS NOT NULL ;

এসকিউএল-ফিডল- এ পরীক্ষা করে দেখায় যে ক্যোয়ারী সূচকে স্ক্যান করে (group_id, ts)

অনুরূপ পরিকল্পনাগুলি 2 পার্শ্বীয় যোগদান করে, একটি ন্যূনতম জন্য এবং একটি সর্বাধিক এবং 2 ইনলাইন সহকৃত সাবকোয়ারি ব্যবহার করে উত্পাদিত হয়। আপনার যদি counterন্যূনতম এবং সর্বাধিক তারিখের পাশাপাশি পুরো সারিগুলি দেখানোর প্রয়োজন হয় তবে সেগুলিও ব্যবহার করা যেতে পারে :

SELECT group_id, 
       min_ts, min_ts_id, 
       max_ts, max_ts_id 
FROM   groups g
  , LATERAL 
       ( SELECT ts AS min_ts, c.id AS min_ts_id
         FROM counter c
         WHERE c.group_id = g.group_id
           AND c.ts BETWEEN timestamp '2012-03-02 00:00:00'
                        AND timestamp '2014-03-05 12:00:00'
         ORDER BY ts ASC
         LIMIT 1
       ) xmin
  , LATERAL 
       ( SELECT ts AS max_ts, c.id AS max_ts_id
         FROM counter c
         WHERE c.group_id = g.group_id
           AND c.ts BETWEEN timestamp '2012-03-02 00:00:00'
                        AND timestamp '2014-03-05 12:00:00'
         ORDER BY ts DESC 
         LIMIT 1
       ) xmax
WHERE min_ts IS NOT NULL ;

@ টাইপারকিউব আমি আপনার প্রশ্নের জন্য ক্যোয়ারী প্ল্যানটি মূল প্রশ্নের সাথে যুক্ত করেছি। কোয়েরিটি 50 এমএসের নীচে এমনকি বড় সময়ের মধ্যেও চলে।
uldall

5

যেহেতু বাছাই তালিকায় আপনার কোনও সমষ্টি নেই, তাই এটি group byএকটি রাখার মতোই একই রকমdistinct না?

যদি আপনি এটি চান তবে আপনি পোস্টগ্রিসএসকিউএল উইকিতে বর্ণিত একটি পুনরাবৃত্ত ক্যোয়ারী ব্যবহার করতে পুনরায় লিখে কমপেক্স_আইডেক্সে দ্রুত সূচক অনুসন্ধান পেতে সক্ষম হতে পারেন

স্বতন্ত্র গ্রুপ_আইডিকে দক্ষতার সাথে ফিরিয়ে দেওয়ার জন্য একটি দৃশ্য তৈরি করুন:

create or replace view groups as
WITH RECURSIVE t AS (
             SELECT min(counter.group_id) AS group_id
               FROM counter
    UNION ALL
             SELECT ( SELECT min(counter.group_id) AS min
                       FROM counter
                      WHERE counter.group_id > t.group_id) AS min
               FROM t
              WHERE t.group_id IS NOT NULL
    )
     SELECT t.group_id
       FROM t
      WHERE t.group_id IS NOT NULL
UNION ALL
     SELECT NULL::bigint AS col
      WHERE (EXISTS ( SELECT counter.id,
                counter.ts,
                counter.group_id
               FROM counter
              WHERE counter.group_id IS NULL));

এবং তারপরে এরউইনের existsআধা- যোগে লুক টেবিলের জায়গায় সেই দৃশ্যটি ব্যবহার করুন ।


4

যেহেতু কেবলমাত্র আছে তাই 133 different group_id'sআপনি গ্রুপ_আইডের জন্য integer(বা এমনকি smallint) ব্যবহার করতে পারেন । এটি আপনাকে খুব বেশি কিনবে না, কারণ 8 বাইটে প্যাডিংটি আপনার টেবিলের মধ্যে বাকী অংশ এবং সম্ভাব্য মাল্টিকোলোম সূচকগুলি খাবে। integerযদিও সমতল প্রক্রিয়াজাতকরণটি কিছুটা দ্রুত হওয়া উচিত। আরো intবনামint2

CREATE TABLE counter (
    id bigserial PRIMARY KEY
  , ts timestamp NOT NULL
  , group_id int NOT NULL
);

@ লিও: টাইমস্ট্যাম্পগুলি আধুনিক ইনস্টলেশনগুলিতে 8-বাইট পূর্ণসংখ্যা হিসাবে সংরক্ষণ করা হয় এবং পুরোপুরি দ্রুত প্রক্রিয়া করা যায়। বিস্তারিত বিবরণ।

@ টাইপ्यूब: সূচকটি (group_id, ts)সাহায্য করতে পারে না, যেহেতু কোয়েরিতে কোনও শর্ত নেই group_id

আপনার মূল সমস্যাটি হ'ল বিপুল পরিমাণে ডেটা যা প্রক্রিয়া করতে হবে:

কাউন্টারে ts_index ব্যবহার করে সূচক স্ক্যান (ব্যয় = 0.56..467470.93 সারি = 194892 প্রস্থ = 4)

আমি দেখতে পাচ্ছি যে আপনি কেবলমাত্র অস্তিত্বের বিষয়ে আগ্রহী group_id, এবং কোনও প্রকৃত গণনা নেই। এছাড়াও, কেবলমাত্র 133 টি বিভিন্ন group_id। সুতরাং আপনার ক্যোয়ারী gorup_idসময় ফ্রেমের প্রতি প্রথম হিট দিয়ে সন্তুষ্ট হতে পারে । সুতরাং একটি EXISTSআধা-যোগদানের সাথে বিকল্প প্রশ্নের জন্য এই পরামর্শ :

দলগুলির জন্য একটি সারণী অনুমান করা:

SELECT group_id
FROM   groups g
WHERE  EXISTS (
   SELECT 1
   FROM   counter c
   WHERE  c.group_id = g.group_id
   AND    ts BETWEEN timestamp '2014-03-02 00:00:00'
                 AND timestamp '2014-03-05 12:00:00'
   );

তোমার সূচক comp_2_indexউপর (group_id, ts)এখন যান্ত্রিক হয়ে যায়।

এসকিউএল ফিডল (মন্তব্যগুলিতে @ টাইপকিউব দ্বারা সরবরাহিত ফিটল উপর বিল্ডিং)

এখানে, ক্যোয়ারিটি সূচকে অগ্রাধিকার দেয় (ts, group_id)তবে আমি মনে করি এটি "ক্লাস্টারড" টাইমস্ট্যাম্পগুলির সাথে পরীক্ষা সেটআপের কারণে। আপনি যদি শীর্ষস্থানীয় সূচকগুলি সরিয়ে ফেলেন ts(সে সম্পর্কে আরও ), পরিকল্পনাকারী আনন্দের সাথে সূচকটিও ব্যবহার করবেন (group_id, ts)- উল্লেখযোগ্যভাবে কেবল একটি সূচক স্ক্যানে

যদি এটি কাজ করে তবে আপনার অন্য এই সম্ভাব্য উন্নতির প্রয়োজন না পড়তে পারে: সারিগুলির সংখ্যা মারাত্মকভাবে হ্রাস করতে একটি বস্তুগত দৃশ্যে প্রাক-সমষ্টিগত ডেটা । এটি আপনার বিশেষ করে যদি অতিরিক্ত অতিরিক্ত গণনাগুলির প্রয়োজন হয় তবে এটি বিশেষত অর্থবোধ করবে । তারপরে আপনার অনেকগুলি প্রক্রিয়া করার জন্য ব্যয় করতে হবে এমভি আপডেট করার সময় একবারে সারি । এমনকি আপনি দৈনিক এবং ঘন্টার জন্য সমষ্টিগুলি (দুটি পৃথক সারণী) একত্রিত করতে পারেন এবং আপনার ক্যোয়ারিকে এটির সাথে মানিয়ে নিতে পারেন।

আপনার প্রশ্নে সময় ফ্রেমগুলি নির্বিচারে হয়? বা বেশিরভাগ পুরো মিনিট / ঘন্টা / দিন?

CREATE MATERIALIZED VIEW counter_mv AS
SELECT date_trunc('hour', ts) AS hour
     , group_id
     , count(*) AS ct
GROUP BY 1,2
ORDER BY 1,2;

counter_mvএটিতে প্রয়োজনীয় সূচি (এস) তৈরি করুন এবং এটির সাথে কাজ করার জন্য আপনার ক্যোয়ারিকে অভিযোজিত করুন ...


1
আমি এসকিউএল-ফিডলগুলিতে 10 কে সারি সহ বেশ কয়েকটি অনুরূপ জিনিস চেষ্টা করেছি , তবে সমস্ত কিছু ক্রমিক স্ক্যান দেখিয়েছে। groupsটেবিল ব্যবহার করে কি তফাত হয়?
ypercubeᵀᴹ

@ টাইপারকিউব: আমারও তাই মনে হয় এছাড়াও, ANALYZEএকটি পার্থক্য তোলে। আমি counterএমনকি টেবিলটি ANALYZEপ্রবর্তন করার সাথে সাথে এমনকি সূচিগুলিকে ব্যবহার না করেই ব্যবহার করতে পারি groups। পয়েন্টটি হল, সেই টেবিলটি ছাড়াই, সম্ভাব্য গ্রুপ_আইডি'র সেটটি তৈরি করতে যাইহোক কোনও সিকস্ক্যান প্রয়োজন। আমি আমার উত্তরে আরও যুক্ত করেছি। এবং আপনার মজার জন্য ধন্যবাদ!
এরউইন ব্র্যান্ডসেটেটার

অদ্ভুত জিনিস. আপনি বলছেন যে পোস্টগ্রিসের অপ্টিমাইজার group_idএমনকি কোনও SELECT DISTINCT group_id FROM t;প্রশ্নের জন্য সূচকটি ব্যবহার করবে না ?
ypercubeᵀᴹ

1
@ ইরউইন ব্র্যান্ডসটেটার এটিই আমি ভাবলাম এবং অন্যথায় তা জানতে পেরে খুব অবাক হয়েছিল। এটি ছাড়া LIMIT 1এটি একটি বিটম্যাপ সূচক স্ক্যান চয়ন করতে পারে, যা তাড়াতাড়ি থামানো থেকে উপকৃত হয় না এবং অনেক বেশি সময় নেয়। (তবে যদি টেবিলটি নতুনভাবে শূন্য হয়, তবে এটি বিটম্যাপ স্ক্যানের চেয়ে সূচী স্ক্যানটিকে পছন্দ করতে পারে, সুতরাং আপনি যে আচরণটি দেখছেন তা টেবিলের শূন্যতার উপর নির্ভর করে)।
jjanes

1
@ অলডাল: প্রতিদিনের সংস্থাগুলি সারিগুলির সংখ্যা মারাত্মকভাবে হ্রাস করবে। কৌতুক করা উচিত। তবে উপস্থিতি-জিজ্ঞাসাটি একবার চেষ্টা করে দেখুন। এটি আশ্চর্যজনকভাবে দ্রুত হতে পারে। অতিরিক্তভাবে কমপক্ষে / সর্বাধিক কাজ করবে না। আমি ফলস্বরূপ পারফরম্যান্সে আগ্রহী হব, যদিও আপনি যদি এখানে একটি লাইন ফেলে দেওয়ার মতো দয়া করেন।
এরউইন ব্র্যান্ডসেটেটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.