পোস্টগ্রিএসকিউএল-তে কীভাবে দ্রুত DISTINCT তৈরি করবেন?


13

station_logsপোস্টগ্র্রেএসকিউএল 9.6 ডাটাবেসে আমার একটি টেবিল রয়েছে:

    Column     |            Type             |    
---------------+-----------------------------+
 id            | bigint                      | bigserial
 station_id    | integer                     | not null
 submitted_at  | timestamp without time zone | 
 level_sensor  | double precision            | 
Indexes:
    "station_logs_pkey" PRIMARY KEY, btree (id)
    "uniq_sid_sat" UNIQUE CONSTRAINT, btree (station_id, submitted_at)

আমি প্রতিটির level_sensorউপর ভিত্তি করে সর্বশেষ মান পাওয়ার চেষ্টা করছি । এখানে প্রায় 400 অনন্য মান রয়েছে এবং প্রতিদিন প্রায় 20k সারি রয়েছে ।submitted_atstation_idstation_idstation_id

সূচক তৈরির আগে:

EXPLAIN ANALYZE
SELECT DISTINCT ON(station_id) station_id, submitted_at, level_sensor
FROM station_logs ORDER BY station_id, submitted_at DESC;
 অনন্য (ব্যয় = 4347852.14..4450301.72 সারি = 89 প্রস্থ = 20) (আসল সময় = 22202.080..27619.167 সারি = 98 লুপ = 1)
   -> বাছাই করুন (ব্যয় = 4347852.14..4399076.93 সারি = 20489916 প্রস্থ = 20) (আসল সময় = 22202.077..26540.827 সারি = 20489812 লুপ = 1)
         কী বাছাই করুন: স্টেশন_আইডি, জমা দেওয়া_আর ডিইএসসি
         বাছাই করার পদ্ধতি: বাহ্যিক মার্জ ডিস্ক: 681040 কেবি
         -> স্টেশন_লগগুলিতে সিক স্ক্যান (ব্যয় = 0.00..598895.16 সারি = 20489916 প্রস্থ = 20) (আসল সময় = 0.023..3443.587 সারি = 20489812 লুপ = $
 পরিকল্পনার সময়: 0.072 এমএস
 সম্পাদনের সময়: 27690.644 এমএস

সূচক তৈরি করা:

CREATE INDEX station_id__submitted_at ON station_logs(station_id, submitted_at DESC);

সূচি তৈরির পরে, একই প্রশ্নের জন্য:

 অনন্য (ব্যয় = 0.56..2156367.51 সারি = 89 প্রস্থ = 20) (আসল সময় = 0.184..16263.413 সারি = 98 লুপ = 1)
   -> স্টেশন_লগগুলিতে স্টেশন_আইডি__ জমা দেওয়া_এট ব্যবহার করে সূচক স্ক্যান (দাম = 0.56..2105142.98 সারি = 20489812 প্রস্থ = 20) (আসল সময় = 0.181..1 $
 পরিকল্পনার সময়: 0.206 এমএস
 সম্পাদনের সময়: 16263.490 এমএস

এই কোয়েরিটি আরও দ্রুত করার কোনও উপায় আছে? উদাহরণস্বরূপ 1 সেকেন্ডের মতো, 16 সেকেন্ড এখনও অনেক বেশি।


2
কতগুলি স্বতন্ত্র স্টেশন আইডি রয়েছে, যেমন কোয়েরিটি কত সারি দেয়? এবং পোস্টগ্রিসের কোন সংস্করণ?
ypercubeᵀᴹ

পোস্টগ্রেয়ার 9.6, প্রায় 400 অনন্য স্টেশন_আইড, এবং স্টেশন_আইডি প্রতিদিন 20k রেকর্ড
কোকিজু

এই প্রশ্নের সাথে ফেরৎ একটি "submitted_at উপর ভিত্তি করে গত level_sensor মান প্রতিটি station_id জন্য"। DISTINCT ON এর ক্ষেত্রে এলোমেলো পছন্দ জড়িত যেখানে আপনার এটির প্রয়োজন নেই in
ফিলিপ্সি

উত্তর:


18

কেবল 400 টি স্টেশনের জন্য, এই ক্যোয়ারী ব্যাপকভাবে দ্রুত হবে:

SELECT s.station_id, l.submitted_at, l.level_sensor
FROM   station s
CROSS  JOIN LATERAL (
   SELECT submitted_at, level_sensor
   FROM   station_logs
   WHERE  station_id = s.station_id
   ORDER  BY submitted_at DESC NULLS LAST
   LIMIT  1
   ) l;

এখানে ডিবিফিডল
(এই ক্যোয়ারির জন্য পরিকল্পনাগুলির তুলনা, অ্যাবেলিস্টোর বিকল্প এবং আপনার মূল) তুলনা করুন)

EXPLAIN ANALYZEওপি দ্বারা সরবরাহিত ফলাফল :

 নেস্টেড লুপ (ব্যয় = 0.56..356.65 সারি = 102 প্রস্থ = 20) (আসল সময় = 0.034..0.979 সারি = 98 লুপ = 1)
   -> স্টেশনগুলিতে সিক স্ক্যান (ব্যয় = 0.00..3.02 সারি = 102 প্রস্থ = 4) (আসল সময় = 0.009..0.016 সারি = 102 লুপ = 1)
   -> সীমা (খরচ = 0.56..3.45 সারি = 1 প্রস্থ = 16) (আসল সময় = 0.009..0.009 সারি = 1 লুপ = 102)
         -> স্টেশন_লগগুলিতে স্টেশন_আইডি__ জমা দেওয়া_এট ব্যবহার করে সূচক স্ক্যান (দাম = 0.56..664062.38 সারি = 230223 প্রস্থ = 16) (আসল সময় = 0.009 $
               সূচকের অবস্থা: (স্টেশন_আইডি = এস.আইডি)
 পরিকল্পনার সময়: 0.542 এমএসফাঁসির 
 সময়: 1.013 এমএস   - !!

শুধুমাত্র সূচক আপনি প্রয়োজন এক আপনার তৈরি করা হল: station_id__submitted_atUNIQUEবাধ্যতা uniq_sid_satএছাড়াও পেশা আছে, মূলত। উভয়ই বজায় রাখা ডিস্কের জায়গার অপচয় এবং লেখার পারফরম্যান্সের মতো মনে হয়।

আমি যোগ NULLS LASTকরতে ORDER BYকারণ ক্যোয়ারীতে submitted_atসংজ্ঞায়িত করা হয় না NOT NULL। আদর্শভাবে, যদি প্রযোজ্য হয়! NOT NULLকলামে একটি সীমাবদ্ধতা যুক্ত করুন submitted_at, অতিরিক্ত সূচকটি ছেড়ে দিন NULLS LASTএবং ক্যোয়ারী থেকে সরিয়ে দিন ।

যদি submitted_atহতে পারে তবে আপনার বর্তমান সূচি এবং অনন্য বাধা উভয়ই প্রতিস্থাপন NULLকরতে এই UNIQUEসূচকটি তৈরি করুন :

CREATE UNIQUE INDEX station_logs_uni ON station_logs(station_id, submitted_at DESC NULLS LAST);

বিবেচনা:

এটি প্রাসঙ্গিক প্রতি এক সারি (সাধারণত পিকে) সহ একটি পৃথক টেবিলstation ধরে station_idনিচ্ছে - যা আপনার উভয় দিক দিয়েই হওয়া উচিত। আপনার যদি এটি না থাকে তবে এটি তৈরি করুন। আবার, এই আরসিটিই কৌশলটি দিয়ে খুব দ্রুত:

CREATE TABLE station AS
WITH RECURSIVE cte AS (
   (
   SELECT station_id
   FROM   station_logs
   ORDER  BY station_id
   LIMIT  1
   )
   UNION ALL
   SELECT l.station_id
   FROM   cte c
   ,      LATERAL (   
      SELECT station_id
      FROM   station_logs
      WHERE  station_id > c.station_id
      ORDER  BY station_id
      LIMIT  1
      ) l
   )
TABLE cte;

আমি হ'ল ফিডেলেও এটি ব্যবহার করি। আপনি stationটেবিল ছাড়াই সরাসরি নিজের টাস্কটি সমাধান করতে অনুরূপ ক্যোয়ারী ব্যবহার করতে পারেন - যদি আপনি এটি তৈরির বিষয়ে নিশ্চিত হতে না পারেন তবে।

বিস্তারিত নির্দেশাবলী, ব্যাখ্যা এবং বিকল্পগুলি:

সূচক অনুকূলিত করুন

আপনার ক্যোয়ারী এখন খুব দ্রুত হওয়া উচিত। আপনার যদি এখনও পড়ার পারফরম্যান্সটিকে অপ্টিমাইজ করা প্রয়োজন ...

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

তবে , আমি আশা করি না যে এটি আপনার ক্ষেত্রে কাজ করবে for তুমি উল্লেখ করেছিলে:

... প্রতিদিন প্রায় 20k সারি station_id

সাধারণত, এটি নিরবচ্ছিন্ন লেখার বোঝা (প্রতি station_id5 সেকেন্ডে 1 জন ) নির্দেশ করবে। এবং আপনি সর্বশেষ সারিতে আগ্রহী । সূচক-কেবলমাত্র স্ক্যানগুলি হ'ল পৃষ্ঠাগুলির জন্যই কাজ করে যা সমস্ত লেনদেনের জন্য দৃশ্যমান (দৃশ্যমান মানচিত্রে কিছুটা সেট করা আছে)। VACUUMলেখার বোঝাটি ধরে রাখতে আপনাকে টেবিলের জন্য অত্যন্ত আক্রমণাত্মক সেটিংস চালাতে হবে এবং এটি বেশিরভাগ সময় কাজ করবে না। যদি আমার অনুমানগুলি সঠিক হয় তবে কেবলমাত্র সূচি-স্ক্যানগুলি বাইরে চলে গেছে, সূচীতে যুক্ত করবেন নাlevel_sensor

ওহ, যদি আমার অনুমানগুলি ধরে থাকে এবং আপনার টেবিলটি খুব বড় আকার ধারণ করছে তবে একটি ব্রিন সূচক সাহায্য করতে পারে। সম্পর্কিত:

বা, আরও বেশি বিশেষজ্ঞ এবং আরও দক্ষ: কেবলমাত্র অপ্রাসঙ্গিক সারিগুলি কাটাতে সর্বশেষ সংযোজনগুলির একটি আংশিক সূচক:

CREATE INDEX station_id__submitted_at_recent_idx ON station_logs(station_id, submitted_at DESC NULLS LAST)
WHERE submitted_at > '2017-06-24 00:00';

এমন টাইমস্ট্যাম্প চয়ন করুন যার জন্য আপনি জানেন যে কম বয়সী সারিগুলি অবশ্যই বিদ্যমান। WHEREআপনাকে সমস্ত প্রশ্নের সাথে মিলে যাওয়ার শর্ত যুক্ত করতে হবে, যেমন:

...
WHERE  station_id = s.station_id
AND    submitted_at > '2017-06-24 00:00'
...

আপনাকে সময়ে সময়ে সূচি এবং ক্যোয়ারীটি মানিয়ে নিতে হবে।
আরও বিশদ সহ সম্পর্কিত উত্তর:


যে কোনও সময় আমি জানতে পারি যে আমি নেস্টেড লুপ চাই (প্রায়শই), LATERAL ব্যবহার করা বেশ কয়েকটি পরিস্থিতির জন্য একটি পারফরম্যান্স উত্সাহ।
পল

6

ক্লাসিক উপায়ে চেষ্টা করুন:

create index idx_station_logs__station_id on station_logs(station_id);
create index idx_station_logs__submitted_at on station_logs(submitted_at);

analyse station_logs;

with t as (
  select station_id, max(submitted_at) submitted_at 
  from station_logs 
  group by station_id)
select * 
from t join station_logs l on (
  l.station_id = t.station_id and l.submitted_at = t.submitted_at);

dbfiddle

থ্রেডস্টার্টার দ্বারা বিশ্লেষণ বিশ্লেষণ করুন

 Nested Loop  (cost=701344.63..702110.58 rows=4 width=155) (actual time=6253.062..6253.544 rows=98 loops=1)
   CTE t
     ->  HashAggregate  (cost=701343.18..701344.07 rows=89 width=12) (actual time=6253.042..6253.069 rows=98 loops=1)
           Group Key: station_logs.station_id
           ->  Seq Scan on station_logs  (cost=0.00..598894.12 rows=20489812 width=12) (actual time=0.034..1841.848 rows=20489812 loop$
   ->  CTE Scan on t  (cost=0.00..1.78 rows=89 width=12) (actual time=6253.047..6253.085 rows=98 loops=1)
   ->  Index Scan using station_id__submitted_at on station_logs l  (cost=0.56..8.58 rows=1 width=143) (actual time=0.004..0.004 rows=$
         Index Cond: ((station_id = t.station_id) AND (submitted_at = t.submitted_at))
 Planning time: 0.542 ms
 Execution time: 6253.701 ms
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.