সাবগ্রিরি যুক্ত করা হলে পোস্টগ্রিজএসকিউএল কোয়েরিটি খুব ধীর


10

1.5M সারি সহ একটি টেবিলে আমার তুলনামূলকভাবে সহজ প্রশ্ন রয়েছে:

SELECT mtid FROM publication
WHERE mtid IN (9762715) OR last_modifier=21321
LIMIT 5000;

EXPLAIN ANALYZE আউটপুট:

Limit  (cost=8.84..12.86 rows=1 width=8) (actual time=0.985..0.986 rows=1 loops=1)
  ->  Bitmap Heap Scan on publication  (cost=8.84..12.86 rows=1 width=8) (actual time=0.984..0.985 rows=1 loops=1)
        Recheck Cond: ((mtid = 9762715) OR (last_modifier = 21321))
        ->  BitmapOr  (cost=8.84..8.84 rows=1 width=0) (actual time=0.971..0.971 rows=0 loops=1)
              ->  Bitmap Index Scan on publication_pkey  (cost=0.00..4.42 rows=1 width=0) (actual time=0.295..0.295 rows=1 loops=1)
                    Index Cond: (mtid = 9762715)
              ->  Bitmap Index Scan on publication_last_modifier_btree  (cost=0.00..4.42 rows=1 width=0) (actual time=0.674..0.674 rows=0 loops=1)
                    Index Cond: (last_modifier = 21321)
Total runtime: 1.027 ms

এখন পর্যন্ত এত ভাল, দ্রুত এবং উপলব্ধ সূচকগুলি ব্যবহার করে।
এখন, যদি আমি একটি ক্যোয়ারিকে কিছুটা সংশোধন করি তবে ফলাফলটি হবে:

SELECT mtid FROM publication
WHERE mtid IN (SELECT 9762715) OR last_modifier=21321
LIMIT 5000;

EXPLAIN ANALYZEআউটপুট হল:

Limit  (cost=0.01..2347.74 rows=5000 width=8) (actual time=2735.891..2841.398 rows=1 loops=1)
  ->  Seq Scan on publication  (cost=0.01..349652.84 rows=744661 width=8) (actual time=2735.888..2841.393 rows=1 loops=1)
        Filter: ((hashed SubPlan 1) OR (last_modifier = 21321))
        SubPlan 1
          ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.001 rows=1 loops=1)
Total runtime: 2841.442 ms

এত দ্রুত নয়, এবং সিক স্ক্যান ব্যবহার করা হচ্ছে ...

অবশ্যই, অ্যাপ্লিকেশন দ্বারা চালিত মূল ক্যোয়ারীটি কিছুটা জটিল এবং আরও ধীর এবং অবশ্যই হাইবারনেট-উত্পাদিত আসলটি নয় (SELECT 9762715), তবে এর জন্যও অস্তিত্ব রয়েছে (SELECT 9762715)! ক্যোয়ারী হাইবারনেট দ্বারা উত্পাদিত হয়েছে, সুতরাং এগুলি পরিবর্তন করা বেশ চ্যালেঞ্জ, এবং কিছু বৈশিষ্ট্য উপলব্ধ নেই (যেমন UNIONউপলব্ধ নয়, যা দ্রুত হবে)।

প্রশ্নসমুহ

  1. দ্বিতীয় ক্ষেত্রে সূচকটি কেন ব্যবহার করা যাবে না? তারা কিভাবে ব্যবহার করা যেতে পারে?
  2. আমি কি অন্যভাবে কোয়েরি পারফরম্যান্স উন্নত করতে পারি?

অতিরিক্ত চিন্তা

দেখে মনে হচ্ছে আমরা প্রথম কেসটি ম্যানুয়ালি একটি নির্বাচন করে, এবং তারপরে ফলাফলের তালিকাটি কোয়েরিতে রেখেছি। এমনকি IN () তালিকায় 5000 নম্বরের সাথে এটি দ্বিতীয় সমাধানের চেয়ে চারগুণ দ্রুত। তবে, এটি কেবল ভুল বলে মনে হচ্ছে (এছাড়াও এটি 100 গুণ দ্রুত হতে পারে :))। কেন এই ক্যোয়ারি পরিকল্পনাকারী এই দুটি প্রশ্নের জন্য সম্পূর্ণ ভিন্ন পদ্ধতি ব্যবহার করেন এটি সম্পূর্ণই বোধগম্য, সুতরাং আমি এই সমস্যার একটি ভাল সমাধান খুঁজে পেতে চাই।


আপনি কি কোনওভাবে আপনার কোডটি আবার লিখতে পারেন যাতে হাইবারনেট এর JOINপরিবর্তে একটি উত্পন্ন করে IN ()? এছাড়াও, publicationসম্প্রতি বিশ্লেষণ করা হয়েছে?
dezso

হ্যাঁ, আমি ভ্যাকুয়াম অ্যানালাইজ এবং ভ্যাকুয়াম ফুল উভয়ই করেছি। পারফরম্যান্সে কোনও পরিবর্তন হয়নি। দ্বিতীয় হিসাবে, AFAIR আমরা এটি চেষ্টা করেছি এবং এটি কোয়েরি কার্যকারিতাটিতে উল্লেখযোগ্যভাবে প্রভাব ফেলেনি।
P.Péter

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

আমি নীচে উল্লেখ করা হয়েছে, এটা ধীর এমনকি যদি ভেতরের ক্যোয়ারী হয় (SELECT 9762715) । হাইবারনেট প্রশ্নের কাছে: এটি করা যেতে পারে তবে আমাদের মারাত্মক কোড পুনর্লিখনের প্রয়োজন রয়েছে, কারণ আমাদের কাছে ব্যবহারকারী-সংজ্ঞায়িত হাইবারনেট মানদণ্ড রয়েছে যা অন-ফ্লাইয়ে অনুবাদ করা হয়। সুতরাং মূলত আমরা হাইবারনেটটি সংশোধন করব যা সম্ভাব্য পার্শ্ব প্রতিক্রিয়াগুলির সাথে একটি বিশাল উদ্যোগ।
P.Péter

উত্তর:


6

সমস্যার মূল বিষয়টি এখানে স্পষ্ট হয়ে ওঠে:

প্রকাশনায় সিক স্ক্যান (ব্যয় = 0.01..349652.84 সারি = 744661 প্রস্থ = 8) (আসল সময় = 2735.888..2841.393 সারি = 1 লুপ = 1)

Postgres অনুমান 744661 সারি ফিরে যাওয়ার সময়, আসলে, এটি সক্রিয় আউট একটি সারি যাবে। পোস্টগ্রিস যদি কোয়েরি থেকে কী প্রত্যাশা করে তা ভালভাবে না জানলে এটি আরও ভাল পরিকল্পনা করতে পারে না। আমাদের সত্যিকারের ক্যোয়ারির আড়াল থাকাটি দেখতে হবে (SELECT 9762715)- এবং সম্ভবত সারণির সংজ্ঞা, সীমাবদ্ধতা, কার্ডিনালিটিস এবং ডেটা বিতরণও জানতে পারে। একথাও ঠিক যে, Postgres ভবিষ্যদ্বাণী করা কিভাবে সক্ষম নয় কয়েক সারি এটি দ্বারা ফেরত পাঠানো হবে। এটা কি তার উপর নির্ভর করে ক্যোয়ারী পুনর্লিখন, উপায় হতে পারে হয়

আপনি যদি জানেন যে সাবকিউরিটি nসারিগুলির চেয়ে বেশি কখনই ফিরে আসতে পারে না , আপনি কেবল পোস্টগ্র্রেস ব্যবহার করে বলতে পারেন:

SELECT mtid
FROM   publication
WHERE  mtid IN (SELECT ... LIMIT n) --  OR last_modifier=21321
LIMIT  5000;

তাহলে এন ছোট যথেষ্ট, Postgres (বিটম্যাপ) সূচক স্ক্যান পাল্টে যাবে। তবে এটি কেবল সাধারণ ক্ষেত্রে কাজ করে। ORশর্ত যুক্ত করার সময় কাজ করা বন্ধ করে দেয় : ক্যোয়ারী পরিকল্পনাকারী বর্তমানে এটি মোকাবেলা করতে পারবেন না।

আমি খুব কমই IN (SELECT ...)দিয়ে শুরু করতে ব্যবহার করি । সাধারণত এটির প্রয়োগের আরও ভাল উপায় আছে, প্রায়শই EXISTSআধা-যোগ দিয়ে। কখনও কখনও একটি ( LEFT) JOIN( LATERAL) দিয়ে ...

সুস্পষ্ট workaround ব্যবহার করা হবে UNION, কিন্তু আপনি তা বাতিল। আসল সাবকিউরি এবং অন্যান্য প্রাসঙ্গিক বিবরণ না জেনে আমি আরও কিছু বলতে পারি না।


2
নেই কোন ক্যোয়ারী পিছনে লুকিয়ে (SELECT 9762715) ! যদি আমি সেই সঠিক কোয়েরিটি চালিত করি যা আপনি উপরে দেখেন। অবশ্যই, মূল হাইবারনেট ক্যোয়ারীটি কিছুটা জটিল, তবে আমি (মনে করি) ক্যোয়ারী পরিকল্পনাকারী যেখানে বিপথগামী হয় তা নির্ধারণ করতে সক্ষম হয়েছি, সুতরাং আমি ক্যোয়ারির সেই অংশটি উপস্থাপন করেছি। তবে উপরোক্ত ব্যাখ্যা এবং প্রশ্নগুলি হ'ল ভারব্যাটিম সিটিআরএল-সিভি।
পি.পিটার

দ্বিতীয় অংশ হিসাবে, অভ্যন্তরীণ সীমা কাজ করে না: EXPLAIN ANALYZE SELECT mtid FROM publication WHERE mtid IN (SELECT 9762715 LIMIT 1) OR last_modifier=21321 LIMIT 5000;এছাড়াও একটি ক্রমিক স্ক্যান করে এবং প্রায় 3 সেকেন্ডের জন্যও চালায় ...
P.Péter

@ পি.পিটার: এটি আমার স্থানীয় পরীক্ষায় পোস্টগ্রিস 9.4-এ প্রকৃত উপকৌটির সাথে আমার জন্য কাজ করে। আপনি যা দেখান তা যদি আপনার আসল জিজ্ঞাস্য হয় তবে আপনার সমাধান ইতিমধ্যে রয়েছে: আপনার প্রশ্নের প্রথম প্রশ্নটি সাবকিউয়ের পরিবর্তে ধ্রুবক দিয়ে ব্যবহার করুন।
এরউইন ব্র্যান্ডস্টেটর

আচ্ছা, আমি একটি নতুন পরীক্ষা টেবিলের উপর একটি subquery চেষ্টা: CREATE TABLE test (mtid bigint NOT NULL, last_modifier bigint, CONSTRAINT test_property_pkey PRIMARY KEY (mtid)); CREATE INDEX test_last_modifier_btree ON test USING btree (last_modifier); INSERT INTO test (mtid, last_modifier) SELECT mtid, last_modifier FROM publication;। এবং প্রভাবগুলি এখনও একই প্রশ্নের জন্য রয়েছে test: যে কোনও সাবকিউয়ের ফলাফল একটি সিক স্ক্যানের ... আমি 9.1 এবং 9.4 উভয়ই চেষ্টা করেছি। প্রভাব একই।
পি.প্লেটার

1
@ পিপিটার: আমি আবার পরীক্ষা চালিয়েছি এবং বুঝতে পেরেছি যে আমি ORশর্ত ছাড়াই পরীক্ষা করেছি । কৌশলটি কেবল LIMITসহজ ক্ষেত্রে কাজ করে।
এরউইন ব্র্যান্ডসেটেটার

6

আমার সহকর্মী ক্যোয়ারীটি পরিবর্তন করার একটি উপায় খুঁজে পেয়েছে যাতে এর জন্য একটি সাধারণ পুনর্লিখনের প্রয়োজন হয় এবং এটি যা করা দরকার তা করে, অর্থাৎ এক পদক্ষেপে সাব-সাবক্লট করা, এবং তারপরে ফলাফলের আরও ক্রিয়াকলাপ সম্পাদন করে:

SELECT mtid FROM publication 
WHERE 
  mtid = ANY( (SELECT ARRAY(SELECT 9762715))::bigint[] )
  OR last_modifier=21321
LIMIT 5000;

ব্যাখ্যা বিশ্লেষণ এখন:

 Limit  (cost=92.58..9442.38 rows=2478 width=8) (actual time=0.071..0.074 rows=1 loops=1)
   InitPlan 2 (returns $1)
     ->  Result  (cost=0.01..0.02 rows=1 width=0) (actual time=0.010..0.011 rows=1 loops=1)
           InitPlan 1 (returns $0)
             ->  Result  (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=1 loops=1)
   ->  Bitmap Heap Scan on publication  (cost=92.56..9442.36 rows=2478 width=8) (actual time=0.069..0.070 rows=1 loops=1)
         Recheck Cond: ((mtid = ANY (($1)::bigint[])) OR (last_modifier = 21321))
         Heap Blocks: exact=1
         ->  BitmapOr  (cost=92.56..92.56 rows=2478 width=0) (actual time=0.060..0.060 rows=0 loops=1)
               ->  Bitmap Index Scan on publication_pkey  (cost=0.00..44.38 rows=10 width=0) (actual time=0.046..0.046 rows=1 loops=1)
                     Index Cond: (mtid = ANY (($1)::bigint[]))
               ->  Bitmap Index Scan on publication_last_modifier_btree  (cost=0.00..46.94 rows=2468 width=0) (actual time=0.011..0.011 rows=0 loops=1)
                     Index Cond: (last_modifier = 21321)
 Planning time: 0.704 ms
 Execution time: 0.153 ms

দেখে মনে হচ্ছে আমরা একটি সাধারণ পার্সার তৈরি করতে পারি যা এইভাবে সমস্ত সাবলেটকে সন্ধান করে এবং পুনরায় লেখতে পারে এবং স্থানীয় কোয়েরিটি পরিচালনা করতে এটি হাইবারনেট হুকের সাথে যুক্ত করতে পারি।


এটা শুনতে দারুণ. SELECTপ্রশ্নটিতে আপনার প্রথম ক্যোয়ারীতে যেমন রয়েছে তবে কেবলমাত্র সমস্ত গুলি মুছে ফেলা কি সহজ নয় ?
dezso

অবশ্যই, আমি একটি দ্বি-পদক্ষেপের পদ্ধতিটি করতে পারি: SELECTআলাদাভাবে করুন এবং তারপরে স্ট্যাটিক তালিকার সাথে বাইরের নির্বাচনটি করুন IN। যাইহোক, এটি উল্লেখযোগ্যভাবে ধীর হয় (সাবকিউরিতে কয়েকটি ফলাফলের চেয়ে বেশি হলে 5-10 বার), আপনার অতিরিক্ত নেটওয়ার্কের রাউন্ড-ট্রিপস রয়েছে এবং আপনার পোস্টের অনেকগুলি ফলাফল ফর্ম্যাট হয়েছে এবং তারপরে জাভা সেই ফলাফলগুলি পার্সিং করছে (এবং তারপরে করছেন পিছনে একই আবার)। উপরের সমাধানটি একই শব্দার্থকভাবে করে, যখন পোস্টগ্রিসের ভিতরে প্রক্রিয়াটি রেখে যায়। সর্বোপরি, বর্তমানে এটি আমাদের ক্ষেত্রে ক্ষুদ্রতম পরিবর্তনের সাথে দ্রুততম উপায় বলে মনে হচ্ছে।
P.P Septer

আহ আমি দেখি. আমি যা জানিনা তা হ'ল আপনি একবারে অনেকগুলি আইডি পেতে পারেন।
dezso

1

দ্বিতীয় প্রশ্নের জবাব: হ্যাঁ, আপনি নিজের সাবকোয়রিতে অর্ডার যোগ করতে পারেন, যা ইতিবাচক প্রভাব ফেলবে। তবে এটি পারফরম্যান্সে "EXISTS (subquery)" সমাধানের সিমিলার। সাবকিউরির সাথেও দুটি সারি হওয়ার ফলে একটি উল্লেখযোগ্য পার্থক্য রয়েছে।

SELECT mtid FROM publication
WHERE mtid IN (SELECT #column# ORDER BY #column#) OR last_modifier=21321
LIMIT 5000;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.