বড় পোস্টগ্রিসএসকিউএল টেবিলটিতে COUNT / GROUP-BY এর কার্যকারিতা উন্নত করবেন?


24

আমি পোস্টগ্রিসএসকিউএল 9.2 চালাচ্ছি এবং প্রায় 6,700,000 সারি সহ 12 কলামের সম্পর্ক রয়েছে। এটিতে একটি 3D স্পেসে নোড রয়েছে, প্রত্যেকে ব্যবহারকারীর (যিনি এটি তৈরি করেছেন) রেফারেন্স করছেন। কোন ব্যবহারকারী কয়টি নোড তৈরি করেছেন তা জিজ্ঞাসা করতে আমি নিম্নলিখিত ( explain analyzeআরও তথ্যের জন্য যুক্ত ) করেছি:

EXPLAIN ANALYZE SELECT user_id, count(user_id) FROM treenode WHERE project_id=1 GROUP BY user_id;
                                                    QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=253668.70..253669.07 rows=37 width=8) (actual time=1747.620..1747.623 rows=38 loops=1)
   ->  Seq Scan on treenode  (cost=0.00..220278.79 rows=6677983 width=8) (actual time=0.019..886.803 rows=6677983 loops=1)
         Filter: (project_id = 1)
 Total runtime: 1747.653 ms

আপনি দেখতে পাচ্ছেন, এটি প্রায় 1.7 সেকেন্ড সময় নেয়। এটি ডেটার পরিমাণ বিবেচনা করে খুব খারাপ নয়, তবে আমি ভাবছি এটি আরও উন্নত করা যায় কিনা। আমি ব্যবহারকারীর কলামে একটি বিটি্রি সূচক যুক্ত করার চেষ্টা করেছি, তবে এটি কোনওভাবেই সহায়তা করে নি।

আপনার কাছে বিকল্প পরামর্শ আছে?


সম্পূর্ণতার জন্য, এটি সমস্ত সূচকগুলির সাথে সম্পূর্ণ টেবিল সংজ্ঞা (বিদেশী কী বাধা, রেফারেন্স এবং ট্রিগার ছাড়াই):

    Column     |           Type           |                      Modifiers                    
---------------+--------------------------+------------------------------------------------------
 id            | bigint                   | not null default nextval('concept_id_seq'::regclass)
 user_id       | bigint                   | not null
 creation_time | timestamp with time zone | not null default now()
 edition_time  | timestamp with time zone | not null default now()
 project_id    | bigint                   | not null
 location      | double3d                 | not null
 reviewer_id   | integer                  | not null default (-1)
 review_time   | timestamp with time zone |
 editor_id     | integer                  |
 parent_id     | bigint                   |
 radius        | double precision         | not null default 0
 confidence    | integer                  | not null default 5
 skeleton_id   | bigint                   |
Indexes:
    "treenode_pkey" PRIMARY KEY, btree (id)
    "treenode_id_key" UNIQUE CONSTRAINT, btree (id)
    "skeleton_id_treenode_index" btree (skeleton_id)
    "treenode_editor_index" btree (editor_id)
    "treenode_location_x_index" btree (((location).x))
    "treenode_location_y_index" btree (((location).y))
    "treenode_location_z_index" btree (((location).z))
    "treenode_parent_id" btree (parent_id)
    "treenode_user_index" btree (user_id)

সম্পাদনা: এটি ফলাফল, যখন আমি @ টাইপকিউবের প্রস্তাবিত ক্যোয়ারী (এবং সূচি) ব্যবহার করি (ক্যোয়ারীটি প্রায় 5.3 সেকেন্ড ব্যতীত লাগে EXPLAIN ANALYZE):

EXPLAIN ANALYZE SELECT u.id, ( SELECT COUNT(*) FROM treenode AS t WHERE t.project_id=1 AND t.user_id = u.id ) AS number_of_nodes FROM auth_user As u;
                                                                        QUERY PLAN                                                                     
----------------------------------------------------------------------------------------------------------------------------------------------------------
 Seq Scan on auth_user u  (cost=0.00..6987937.85 rows=46 width=4) (actual time=29.934..5556.147 rows=46 loops=1)
   SubPlan 1
     ->  Aggregate  (cost=151911.65..151911.66 rows=1 width=0) (actual time=120.780..120.780 rows=1 loops=46)
           ->  Bitmap Heap Scan on treenode t  (cost=4634.41..151460.44 rows=180486 width=0) (actual time=13.785..114.021 rows=145174 loops=46)
                 Recheck Cond: ((project_id = 1) AND (user_id = u.id))
                 Rows Removed by Index Recheck: 461076
                 ->  Bitmap Index Scan on treenode_user_index  (cost=0.00..4589.29 rows=180486 width=0) (actual time=13.082..13.082 rows=145174 loops=46)
                       Index Cond: ((project_id = 1) AND (user_id = u.id))
 Total runtime: 5556.190 ms
(9 rows)

Time: 5556.804 ms

সম্পাদনা করুন 2: এই ফল যখন আমি একটি ব্যবহার indexউপর project_id, user_id(কিন্তু কোন স্কিমা অপ্টিমাইজেশান, এখনো) @ এরউইন-brandstetter হিসাবে (আমার মূল প্রশ্নের সাথে একই গতিতে 1.5 সেকেন্ড দিয়ে কোয়েরি রান) প্রস্তাবিত:

EXPLAIN ANALYZE SELECT user_id, count(user_id) as ct FROM treenode WHERE project_id=1 GROUP BY user_id;
                                                        QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=253670.88..253671.24 rows=37 width=8) (actual time=1807.334..1807.339 rows=38 loops=1)
   ->  Seq Scan on treenode  (cost=0.00..220280.62 rows=6678050 width=8) (actual time=0.183..893.491 rows=6678050 loops=1)
         Filter: (project_id = 1)
 Total runtime: 1807.368 ms
(4 rows)

এছাড়াও আপনি একটি টেবিল আছে Usersসঙ্গে user_idপ্রাথমিক কী হিসেবে?
ypercubeᵀᴹ

আমি কেবল দেখেছি পোস্টগ্র্রেসের জন্য একটি তৃতীয় পক্ষের কলামস্টোর অ্যাডন রয়েছে। এছাড়াও, আমি কেবল নতুন
আইওএস

2
সংস্করণ, সারণী সংজ্ঞা ইত্যাদি
ক্রেগ রিঞ্জার

হ্যাঁ, আমি একটি ব্যবহারকারীর টেবিল পেয়েছি।
টমকা

কত আলাদা project_idএবং user_id? টেবিলটি অবিচ্ছিন্নভাবে আপডেট হয়েছে বা আপনি কোনও বস্তুগত দর্শন (কিছু সময়ের জন্য) দিয়ে কাজ করতে পারেন?
এরউইন ব্র্যান্ডসটেটার

উত্তর:


25

মূল সমস্যা হ'ল অনুপস্থিত সূচক। তবে আরও আছে।

SELECT user_id, count(*) AS ct
FROM   treenode
WHERE  project_id = 1
GROUP  BY user_id;
  • আপনার অনেক bigintকলাম রয়েছে। সম্ভবত ওভারকিল সাধারণত, এবং এর integerমতো কলামগুলির জন্য যথেষ্ট বেশি । এটি পরবর্তী আইটেমটিকেও সহায়তা করবে। সারণির সংজ্ঞাটি অনুকূলকরণের সময়, ডাটা অ্যালাইনমেন্ট এবং প্যাডিংয়ের উপর জোর দিয়ে এই সম্পর্কিত উত্তরটি বিবেচনা করুন তবে বাকী বেশিরভাগটি প্রয়োগ হয়:project_iduser_id

  • রুমে হাতি আছে: নেই উপর সূচকproject_id । একটি তৈরী কর. এটি এই উত্তরের বাকীগুলির চেয়ে গুরুত্বপূর্ণ।
    এটি যখন থাকাকালীন, এটি একটি বহুবিধ সূচক তৈরি করুন:

    CREATE INDEX treenode_project_id_user_id_index ON treenode (project_id, user_id);

    আপনি যদি আমার পরামর্শ অনুসরণ করেন integerতবে এখানে নিখুঁত হবে:

  • user_idসংজ্ঞায়িত করা হয় NOT NULL, সুতরাং count(user_id)এর সমতুল্য count(*), তবে পরবর্তীটি কিছুটা খাটো এবং দ্রুত। (এই নির্দিষ্ট ক্যোয়ারিতে, এটি user_idসংজ্ঞায়িত না করেই প্রযোজ্য হবে NOT NULL))

  • idইতোমধ্যে প্রাথমিক কী, অতিরিক্ত UNIQUEসীমাবদ্ধতা অকেজো ব্যালাস্ট । ফেলে দাও:

    "treenode_pkey" PRIMARY KEY, btree (id)
    "treenode_id_key" UNIQUE CONSTRAINT, btree (id)

    পাশে: আমি idকলামের নাম হিসাবে ব্যবহার করব না । বর্ণনামূলক কিছু ব্যবহার করুন treenode_id

যুক্ত তথ্য

প্রশ্ন: How many different project_id and user_id?
উত্তর: not more than five different project_id

তার মানে পোস্টগ্র্রেসকে আপনার ক্যোয়ারীটি পূরণ করতে পুরো টেবিলের প্রায় 20% পড়তে হবে । এটি সূচক-কেবলমাত্র স্ক্যান ব্যবহার না করা অবধি টেবিলে একটি ক্রমিক স্ক্যান কোনও সূচককে যুক্ত করার চেয়ে দ্রুত হবে faster টেবিল এবং সার্ভার সেটিংস অপ্টিমাইজ করা ব্যতীত - এখানে আর কোনও কর্মক্ষমতা অর্জনের নয়।

হিসাবে সূচক শুধুমাত্র স্ক্যান : দেখার জন্য কতটা কার্যকর হতে পারে, রান VACUUM ANALYZEআপনি যে (কেশ একচেটিয়াভাবে টেবিল) সামর্থ পারেন। তারপরে আপনার জিজ্ঞাসাটি আবার চেষ্টা করুন। এটি এখন কেবল সূচকটি ব্যবহার করে মাঝারিভাবে দ্রুত হওয়া উচিত । এই সম্পর্কিত উত্তরটি আগে পড়ুন:

পাশাপাশি ম্যানুয়াল পৃষ্ঠাটি পোস্টগ্রিস 9.6 এবং পোস্টগ্র্রেস উইকির সাথে কেবল সূচক-কেবল স্ক্যানগুলিতে যুক্ত হয়েছে


1
এরউইন, আপনার পরামর্শের জন্য ধন্যবাদ। আপনি সঠিক, জন্য user_idএবং project_id integerযথেষ্ট বেশী হওয়া উচিত। count(*)পরিবর্তে count(user_id)এখানে ব্যবহার প্রায় 70ms সঞ্চয় করে, এটি জেনে রাখা ভাল। আমি জুড়েছেন EXPLAIN ANALYZEপর আমি আপনার প্রস্তাবিত জুড়েছেন ক্যোয়ারী indexপ্রথম পোস্ট করতে। এটি (যদিও ক্ষতি করে না) পারফরম্যান্সের উন্নতি করে না। দেখে মনে হচ্ছে এটি indexমোটেই ব্যবহৃত হয়নি। আমি শিগগির স্কিমা অপটিমাইজেশন পরীক্ষা করব।
টোমকা

1
যদি আমি অক্ষম করি seqscanতবে সূচকটি ব্যবহার করা হয় ( Index Only Scan using treenode_project_id_user_id_index on treenode), তবে ক্যোয়ারিতে প্রায় 2.5 সেকেন্ড সময় লাগে (যা সেকস্ক্যানের তুলনায় প্রায় 1 সেকেন্ড দীর্ঘ)।
টমকা

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

7

আমি প্রথমে একটি সূচি যুক্ত করব (project_id, user_id)এবং তারপরে 9.3 সংস্করণে, এই কোয়েরিটি চেষ্টা করে দেখুন:

SELECT u.user_id, c.number_of_nodes 
FROM users AS u
   , LATERAL
     ( SELECT COUNT(*) AS number_of_nodes 
       FROM treenode AS t
       WHERE t.project_id = 1 
         AND t.user_id = u.user_id
     ) c 
-- WHERE c.number_of_nodes > 0 ;   -- you probably want this as well
                                   -- to show only relevant users

9.2 এ, এটি ব্যবহার করে দেখুন:

SELECT u.user_id, 
       ( SELECT COUNT(*) 
         FROM treenode AS t
         WHERE t.project_id = 1 
           AND t.user_id = u.user_id
       ) AS number_of_nodes  
FROM users AS u ;

আমি ধরে নিলাম আপনার একটা usersটেবিল আছে যদি তা না হয় তবে এর usersসাথে প্রতিস্থাপন করুন :
(SELECT DISTINCT user_id FROM treenode)


উত্তরের জন্য তোমাকে অনেক ধন্যবাদ. আপনি সঠিক, আমি একটি ব্যবহারকারী টেবিল পেয়েছি। যাইহোক, 9.2 এ আপনার ক্যোয়ারীটি ব্যবহার করে ফলাফলটি পেতে প্রায় 5 সেকেন্ড সময় লাগে - সূচকটি তৈরি করা হয়েছে বা না নির্বিশেষে। আমি সূচকটি এই জাতীয়ভাবে তৈরি করেছি: CREATE INDEX treenode_user_index ON treenode USING btree (project_id, user_id);তবে আমি USINGধারাটি ছাড়াই চেষ্টা করেছি । আমি কি কিছু মিস করছি?
টমকা

usersসারণীতে কতগুলি সারি রয়েছে এবং কতগুলি সারি ক্যোয়ারী ফিরে আসে (সুতরাং সেখানে কতজন ব্যবহারকারী রয়েছেন project_id=1)? আপনি সূচকটি যুক্ত করার পরে, আপনি এই ক্যোয়ারির ব্যাখ্যা প্রদর্শন করতে পারেন?
ypercubeᵀᴹ

1
প্রথমত, আমি আমার প্রথম মন্তব্যে ভুল ছিলাম। আপনার প্রস্তাবিত সূচী ছাড়াই ফলাফলটি পুনরুদ্ধার করতে প্রায় 40s (!) লাগে। indexজায়গাটিতে এটি প্রায় 5s লাগে । বিভ্রান্তির জন্য দুঃখিত. আমার usersটেবিলে আমি 46 টি এন্ট্রি পেয়েছি। ক্যোয়ারীটি কেবল 9 টি সারি দেয়। আশ্চর্যজনকভাবে, SELECT DISTINCT user_id FROM treenode WHERE project_id=1;38 টি সারি ফেরত দেয়। আমি explainআমার প্রথম পোস্টে যোগ করেছি । এবং বিভ্রান্তি রোধ করতে: আমার usersটেবিলটি আসলে ডাকা হয় auth_user
টোমকা

আমি ভাবছি কীভাবে SELECT DISTINCT user_id FROM treenode WHERE project_id=1;38 টি সারি ফিরিয়ে আনতে পারে যখন প্রশ্নগুলি 9 টি ফিরে আসে।
ypercubeᵀᴹ

আপনি কি এটি চেষ্টা করতে পারেন ?:SET enable_seqscan = OFF; (Query); SET enable_seqscan = ON;
ypercubeᵀᴹ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.